Merge "cmds: Use new libgui API for display info"
diff --git a/apex/Android.bp b/apex/Android.bp
index 42a620b..73dc264 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -41,6 +41,8 @@
// Use a custom AndroidManifest.xml used for API targeting.
androidManifest: ":com.android.media-androidManifest",
+
+ legacy_android10_support: true,
}
apex {
@@ -76,6 +78,8 @@
// Use a custom AndroidManifest.xml used for API targeting.
androidManifest: ":com.android.media.swcodec-androidManifest",
+
+ legacy_android10_support: true,
}
prebuilt_etc {
diff --git a/apex/AndroidManifest-media.xml b/apex/AndroidManifest-media.xml
index 1af4586..b020cba 100644
--- a/apex/AndroidManifest-media.xml
+++ b/apex/AndroidManifest-media.xml
@@ -19,8 +19,10 @@
<!-- APEX does not have classes.dex -->
<application android:hasCode="false" />
<!-- Setting maxSdk to lock the module to Q. minSdk is auto-set by build system -->
- <uses-sdk
+ <!-- TODO: Uncomment this when the R API level is fixed. b/148281152 -->
+ <!--uses-sdk
android:maxSdkVersion="29"
android:targetSdkVersion="29"
/>
+ -->
</manifest>
diff --git a/apex/AndroidManifest-swcodec.xml b/apex/AndroidManifest-swcodec.xml
index de50864..1a28d19 100644
--- a/apex/AndroidManifest-swcodec.xml
+++ b/apex/AndroidManifest-swcodec.xml
@@ -19,8 +19,10 @@
<!-- APEX does not have classes.dex -->
<application android:hasCode="false" />
<!-- Setting maxSdk to lock the module to Q. minSdk is auto-set by build system -->
- <uses-sdk
+ <!-- TODO: Uncomment this when the R API level is fixed. b/148281152 -->
+ <!--uses-sdk
android:maxSdkVersion="29"
android:targetSdkVersion="29"
/>
+ -->
</manifest>
diff --git a/apex/ld.config.txt b/apex/ld.config.txt
index 1c44e65..713f0b7 100644
--- a/apex/ld.config.txt
+++ b/apex/ld.config.txt
@@ -22,6 +22,12 @@
namespace.default.search.paths = /apex/com.android.media.swcodec/${LIB}
namespace.default.asan.search.paths = /apex/com.android.media.swcodec/${LIB}
+# Below lines are required to be able to access libs in APEXes which are
+# actually symlinks to the files under /system/lib. The symlinks exist for
+# bundled APEXes to reduce space.
+namespace.default.permitted.paths = /system/${LIB}
+namespace.default.asan.permitted.paths = /system/${LIB}
+
namespace.default.links = platform
# TODO: replace the following when apex has a way to auto-generate this list
diff --git a/camera/Android.bp b/camera/Android.bp
index b288bcf..fa36bb3 100644
--- a/camera/Android.bp
+++ b/camera/Android.bp
@@ -40,6 +40,7 @@
"ICameraRecordingProxy.cpp",
"ICameraRecordingProxyListener.cpp",
"camera2/CaptureRequest.cpp",
+ "camera2/ConcurrentCamera.cpp",
"camera2/OutputConfiguration.cpp",
"camera2/SessionConfiguration.cpp",
"camera2/SubmitInfo.cpp",
@@ -66,7 +67,7 @@
"include",
"include/camera"
],
- export_shared_lib_headers: ["libcamera_metadata"],
+ export_shared_lib_headers: ["libcamera_metadata", "libnativewindow", "libgui"],
cflags: [
"-Werror",
@@ -85,6 +86,7 @@
"aidl/android/hardware/ICameraServiceProxy.aidl",
"aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl",
"aidl/android/hardware/camera2/ICameraDeviceUser.aidl",
+ "aidl/android/hardware/camera2/ICameraOfflineSession.aidl",
],
path: "aidl",
}
diff --git a/camera/CameraBase.cpp b/camera/CameraBase.cpp
index c53e6c3..aecb70a 100644
--- a/camera/CameraBase.cpp
+++ b/camera/CameraBase.cpp
@@ -60,6 +60,13 @@
if (res != OK) return res;
res = parcel->writeInt32(status);
+ if (res != OK) return res;
+
+ std::vector<String16> unavailablePhysicalIds16;
+ for (auto& id8 : unavailablePhysicalIds) {
+ unavailablePhysicalIds16.push_back(String16(id8));
+ }
+ res = parcel->writeString16Vector(unavailablePhysicalIds16);
return res;
}
@@ -70,6 +77,14 @@
cameraId = String8(tempCameraId);
res = parcel->readInt32(&status);
+ if (res != OK) return res;
+
+ std::vector<String16> unavailablePhysicalIds16;
+ res = parcel->readString16Vector(&unavailablePhysicalIds16);
+ if (res != OK) return res;
+ for (auto& id16 : unavailablePhysicalIds16) {
+ unavailablePhysicalIds.push_back(String8(id16));
+ }
return res;
}
diff --git a/camera/CameraMetadata.cpp b/camera/CameraMetadata.cpp
index 92fe84b..15c295e 100644
--- a/camera/CameraMetadata.cpp
+++ b/camera/CameraMetadata.cpp
@@ -17,7 +17,9 @@
// #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>
@@ -38,11 +40,13 @@
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);
}
@@ -104,6 +108,7 @@
}
void CameraMetadata::clear() {
+ ATRACE_CALL();
if (mLocked) {
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
return;
@@ -115,6 +120,7 @@
}
void CameraMetadata::acquire(camera_metadata_t *buffer) {
+ ATRACE_CALL();
if (mLocked) {
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
return;
@@ -128,6 +134,7 @@
}
void CameraMetadata::acquire(CameraMetadata &other) {
+ ATRACE_CALL();
if (mLocked) {
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
return;
@@ -136,10 +143,12 @@
}
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;
@@ -161,6 +170,7 @@
}
status_t CameraMetadata::sort() {
+ ATRACE_CALL();
if (mLocked) {
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
return INVALID_OPERATION;
@@ -291,6 +301,7 @@
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__);
@@ -348,11 +359,13 @@
}
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) {
@@ -369,6 +382,7 @@
}
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);
@@ -380,6 +394,7 @@
}
status_t CameraMetadata::erase(uint32_t tag) {
+ ATRACE_CALL();
camera_metadata_entry_t entry;
status_t res;
if (mLocked) {
@@ -410,6 +425,7 @@
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;
@@ -486,6 +502,7 @@
}
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) {
@@ -525,7 +542,7 @@
status_t CameraMetadata::readFromParcel(const Parcel& data,
camera_metadata_t** out) {
-
+ ATRACE_CALL();
status_t err = OK;
camera_metadata_t* metadata = NULL;
@@ -616,6 +633,7 @@
status_t CameraMetadata::writeToParcel(Parcel& data,
const camera_metadata_t* metadata) {
+ ATRACE_CALL();
status_t res = OK;
/**
@@ -710,7 +728,7 @@
}
status_t CameraMetadata::readFromParcel(const Parcel *parcel) {
-
+ ATRACE_CALL();
ALOGV("%s: parcel = %p", __FUNCTION__, parcel);
status_t res = OK;
@@ -742,7 +760,7 @@
}
status_t CameraMetadata::writeToParcel(Parcel *parcel) const {
-
+ ATRACE_CALL();
ALOGV("%s: parcel = %p", __FUNCTION__, parcel);
if (parcel == NULL) {
@@ -771,7 +789,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/aidl/android/hardware/ICameraService.aidl b/camera/aidl/android/hardware/ICameraService.aidl
index 62dbb5e..833893e 100644
--- a/camera/aidl/android/hardware/ICameraService.aidl
+++ b/camera/aidl/android/hardware/ICameraService.aidl
@@ -22,6 +22,8 @@
import android.hardware.camera2.ICameraDeviceCallbacks;
import android.hardware.camera2.params.VendorTagDescriptor;
import android.hardware.camera2.params.VendorTagDescriptorCache;
+import android.hardware.camera2.utils.ConcurrentCameraIdCombination;
+import android.hardware.camera2.utils.CameraIdAndSessionConfiguration;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.ICameraServiceListener;
import android.hardware.CameraInfo;
@@ -114,6 +116,25 @@
CameraStatus[] addListener(ICameraServiceListener listener);
/**
+ * Get a list of combinations of camera ids which support concurrent streaming.
+ *
+ */
+ ConcurrentCameraIdCombination[] getConcurrentStreamingCameraIds();
+
+ /**
+ * Check whether a particular set of session configurations are concurrently supported by the
+ * corresponding camera ids.
+ *
+ * @param sessions the set of camera id and session configuration pairs to be queried.
+ * @return true - the set of concurrent camera id and stream combinations is supported.
+ * false - the set of concurrent camera id and stream combinations is not supported
+ * OR the method was called with a set of camera ids not returned by
+ * getConcurrentMultiStreamingCameraIds().
+ */
+ boolean isConcurrentSessionConfigurationSupported(
+ in CameraIdAndSessionConfiguration[] sessions);
+
+ /**
* Remove listener for changes to camera device and flashlight state.
*/
void removeListener(ICameraServiceListener listener);
diff --git a/camera/aidl/android/hardware/ICameraServiceListener.aidl b/camera/aidl/android/hardware/ICameraServiceListener.aidl
index e9dcbdb..81657fd 100644
--- a/camera/aidl/android/hardware/ICameraServiceListener.aidl
+++ b/camera/aidl/android/hardware/ICameraServiceListener.aidl
@@ -54,6 +54,12 @@
oneway void onStatusChanged(int status, String cameraId);
/**
+ * Notify registered client about status changes for a physical camera backing
+ * a logical camera.
+ */
+ oneway void onPhysicalCameraStatusChanged(int status, String cameraId, String physicalCameraId);
+
+ /**
* The torch mode status of a camera.
*
* Initial status will be transmitted with onTorchStatusChanged immediately
diff --git a/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl b/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
index 93549e0..b183ccc 100644
--- a/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
+++ b/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
@@ -17,6 +17,8 @@
package android.hardware.camera2;
import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.ICameraDeviceCallbacks;
+import android.hardware.camera2.ICameraOfflineSession;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.params.SessionConfiguration;
@@ -81,8 +83,9 @@
* @param operatingMode The kind of session to create; either NORMAL_MODE or
* CONSTRAINED_HIGH_SPEED_MODE. Must be a non-negative value.
* @param sessionParams Session wide camera parameters
+ * @return a list of stream ids that can be used in offline mode via "switchToOffline"
*/
- void endConfigure(int operatingMode, in CameraMetadataNative sessionParams);
+ int[] endConfigure(int operatingMode, in CameraMetadataNative sessionParams);
/**
* Check whether a particular session configuration has camera device
@@ -177,4 +180,15 @@
* @return the currently applied system-wide audio restriction mode
*/
int getGlobalAudioRestriction();
+
+ /**
+ * Offline processing main entry point
+ *
+ * @param callbacks Object that will receive callbacks from offline session
+ * @param offlineOutputIds The ID of streams that needs to be preserved in offline session
+ *
+ * @return Offline session object.
+ */
+ ICameraOfflineSession switchToOffline(in ICameraDeviceCallbacks callbacks,
+ in int[] offlineOutputIds);
}
diff --git a/camera/aidl/android/hardware/camera2/ICameraOfflineSession.aidl b/camera/aidl/android/hardware/camera2/ICameraOfflineSession.aidl
new file mode 100644
index 0000000..ab030ab
--- /dev/null
+++ b/camera/aidl/android/hardware/camera2/ICameraOfflineSession.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2;
+
+ /** @hide */
+interface ICameraOfflineSession
+{
+ void disconnect();
+}
diff --git a/camera/aidl/android/hardware/camera2/utils/CameraIdAndSessionConfiguration.aidl b/camera/aidl/android/hardware/camera2/utils/CameraIdAndSessionConfiguration.aidl
new file mode 100644
index 0000000..ca89ad8
--- /dev/null
+++ b/camera/aidl/android/hardware/camera2/utils/CameraIdAndSessionConfiguration.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+package android.hardware.camera2.utils;
+
+/** @hide */
+parcelable CameraIdAndSessionConfiguration cpp_header "camera/camera2/ConcurrentCamera.h";
diff --git a/camera/aidl/android/hardware/camera2/utils/ConcurrentCameraIdCombination.aidl b/camera/aidl/android/hardware/camera2/utils/ConcurrentCameraIdCombination.aidl
new file mode 100644
index 0000000..4b35c31
--- /dev/null
+++ b/camera/aidl/android/hardware/camera2/utils/ConcurrentCameraIdCombination.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+package android.hardware.camera2.utils;
+
+/** @hide */
+parcelable ConcurrentCameraIdCombination cpp_header "camera/camera2/ConcurrentCamera.h";
diff --git a/camera/camera2/ConcurrentCamera.cpp b/camera/camera2/ConcurrentCamera.cpp
new file mode 100644
index 0000000..01a695c
--- /dev/null
+++ b/camera/camera2/ConcurrentCamera.cpp
@@ -0,0 +1,129 @@
+/*
+ * 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 "ConcurrentCamera"
+#include <utils/Log.h>
+#include <utils/String16.h>
+
+#include <camera/camera2/ConcurrentCamera.h>
+
+#include <binder/Parcel.h>
+
+namespace android {
+namespace hardware {
+namespace camera2 {
+namespace utils {
+
+ConcurrentCameraIdCombination::ConcurrentCameraIdCombination() = default;
+
+ConcurrentCameraIdCombination::ConcurrentCameraIdCombination(
+ std::vector<std::string> &&combination) : mConcurrentCameraIds(std::move(combination)) { }
+
+ConcurrentCameraIdCombination::~ConcurrentCameraIdCombination() = default;
+
+status_t ConcurrentCameraIdCombination::readFromParcel(const android::Parcel* parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ status_t err = OK;
+ mConcurrentCameraIds.clear();
+ int32_t cameraIdCount = 0;
+ if ((err = parcel->readInt32(&cameraIdCount)) != OK) {
+ ALOGE("%s: Failed to read the camera id count from parcel: %d", __FUNCTION__, err);
+ return err;
+ }
+ for (int32_t i = 0; i < cameraIdCount; i++) {
+ String16 id;
+ if ((err = parcel->readString16(&id)) != OK) {
+ ALOGE("%s: Failed to read camera id!", __FUNCTION__);
+ return err;
+ }
+ mConcurrentCameraIds.push_back(std::string(String8(id).string()));
+ }
+ return OK;
+}
+
+status_t ConcurrentCameraIdCombination::writeToParcel(android::Parcel* parcel) const {
+
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ status_t err = OK;
+
+ if ((err = parcel->writeInt32(mConcurrentCameraIds.size())) != OK) {
+ ALOGE("%s: Failed to write the camera id count to parcel: %d", __FUNCTION__, err);
+ return err;
+ }
+
+ for (const auto &it : mConcurrentCameraIds) {
+ if ((err = parcel->writeString16(String16(it.c_str()))) != OK) {
+ ALOGE("%s: Failed to write the camera id string to parcel: %d", __FUNCTION__, err);
+ return err;
+ }
+ }
+ return OK;
+}
+
+CameraIdAndSessionConfiguration::CameraIdAndSessionConfiguration() = default;
+CameraIdAndSessionConfiguration::~CameraIdAndSessionConfiguration() = default;
+
+status_t CameraIdAndSessionConfiguration::readFromParcel(const android::Parcel* parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ status_t err = OK;
+ String16 id;
+ if ((err = parcel->readString16(&id)) != OK) {
+ ALOGE("%s: Failed to read camera id!", __FUNCTION__);
+ return err;
+ }
+ if ((err = mSessionConfiguration.readFromParcel(parcel)) != OK) {
+ ALOGE("%s: Failed to read sessionConfiguration!", __FUNCTION__);
+ return err;
+ }
+ mCameraId = std::string(String8(id).string());
+ return OK;
+}
+
+status_t CameraIdAndSessionConfiguration::writeToParcel(android::Parcel* parcel) const {
+
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ status_t err = OK;
+ if ((err = parcel->writeString16(String16(mCameraId.c_str()))) != OK) {
+ ALOGE("%s: Failed to write camera id!", __FUNCTION__);
+ return err;
+ }
+
+ if ((err = mSessionConfiguration.writeToParcel(parcel) != OK)) {
+ ALOGE("%s: Failed to write session configuration!", __FUNCTION__);
+ return err;
+ }
+ return OK;
+}
+
+} // namespace utils
+} // namespace camera2
+} // namespace hardware
+} // namespace android
diff --git a/camera/cameraserver/Android.bp b/camera/cameraserver/Android.bp
index 320c499..22e9578 100644
--- a/camera/cameraserver/Android.bp
+++ b/camera/cameraserver/Android.bp
@@ -32,6 +32,7 @@
"android.hardware.camera.common@1.0",
"android.hardware.camera.provider@2.4",
"android.hardware.camera.provider@2.5",
+ "android.hardware.camera.provider@2.6",
"android.hardware.camera.device@1.0",
"android.hardware.camera.device@3.2",
"android.hardware.camera.device@3.4",
diff --git a/camera/include/camera/CameraBase.h b/camera/include/camera/CameraBase.h
index 74a2dce..499b0e6 100644
--- a/camera/include/camera/CameraBase.h
+++ b/camera/include/camera/CameraBase.h
@@ -80,10 +80,16 @@
*/
int32_t status;
+ /**
+ * Unavailable physical camera names for a multi-camera device
+ */
+ std::vector<String8> unavailablePhysicalIds;
+
virtual status_t writeToParcel(android::Parcel* parcel) const;
virtual status_t readFromParcel(const android::Parcel* parcel);
- CameraStatus(String8 id, int32_t s) : cameraId(id), status(s) {}
+ CameraStatus(String8 id, int32_t s, const std::vector<String8>& unavailSubIds) :
+ cameraId(id), status(s), unavailablePhysicalIds(unavailSubIds) {}
CameraStatus() : status(ICameraServiceListener::STATUS_PRESENT) {}
};
diff --git a/camera/include/camera/camera2/ConcurrentCamera.h b/camera/include/camera/camera2/ConcurrentCamera.h
new file mode 100644
index 0000000..ac99fd5
--- /dev/null
+++ b/camera/include/camera/camera2/ConcurrentCamera.h
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_CAMERA2_UTIL_CONCURRENTCAMERA_H
+#define ANDROID_HARDWARE_CAMERA2_UTIL_CONCURRENTCAMERA_H
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <camera2/OutputConfiguration.h>
+#include <camera2/SessionConfiguration.h>
+
+namespace android {
+namespace hardware {
+namespace camera2 {
+namespace utils {
+
+struct ConcurrentCameraIdCombination : public Parcelable {
+ std::vector<std::string> mConcurrentCameraIds;
+ ConcurrentCameraIdCombination();
+ ConcurrentCameraIdCombination(std::vector<std::string> &&combination);
+ virtual ~ConcurrentCameraIdCombination();
+
+ virtual status_t writeToParcel(android::Parcel *parcel) const override;
+ virtual status_t readFromParcel(const android::Parcel* parcel) override;
+};
+
+struct CameraIdAndSessionConfiguration : public Parcelable {
+ std::string mCameraId;
+ SessionConfiguration mSessionConfiguration;
+
+ CameraIdAndSessionConfiguration();
+ virtual ~CameraIdAndSessionConfiguration();
+
+ virtual status_t writeToParcel(android::Parcel *parcel) const override;
+ virtual status_t readFromParcel(const android::Parcel* parcel) override;
+};
+
+} // namespace utils
+} // namespace camera2
+} // namespace hardware
+} // namespace android
+
+#endif
diff --git a/camera/ndk/Android.bp b/camera/ndk/Android.bp
index d8220eb..56f209c 100644
--- a/camera/ndk/Android.bp
+++ b/camera/ndk/Android.bp
@@ -57,6 +57,9 @@
"libmediandk",
"libnativewindow",
],
+ header_libs: [
+ "jni_headers",
+ ],
cflags: [
"-fvisibility=hidden",
"-DEXPORT=__attribute__ ((visibility (\"default\")))",
@@ -121,7 +124,6 @@
"android.frameworks.cameraservice.common@2.0",
"android.frameworks.cameraservice.service@2.0",
],
-
static_libs: [
"android.hardware.camera.common@1.0-helper",
"libarect",
diff --git a/camera/ndk/NdkCameraMetadata.cpp b/camera/ndk/NdkCameraMetadata.cpp
index 9a39ed8..1fec3e1 100644
--- a/camera/ndk/NdkCameraMetadata.cpp
+++ b/camera/ndk/NdkCameraMetadata.cpp
@@ -26,6 +26,75 @@
using namespace android;
+#ifndef __ANDROID_VNDK__
+namespace {
+
+constexpr const char* android_hardware_camera2_CameraMetadata_jniClassName =
+ "android/hardware/camera2/CameraMetadata";
+constexpr const char* android_hardware_camera2_CameraCharacteristics_jniClassName =
+ "android/hardware/camera2/CameraCharacteristics";
+constexpr const char* android_hardware_camera2_CaptureResult_jniClassName =
+ "android/hardware/camera2/CaptureResult";
+
+jclass android_hardware_camera2_CameraCharacteristics_clazz = nullptr;
+jclass android_hardware_camera2_CaptureResult_clazz = nullptr;
+jmethodID android_hardware_camera2_CameraMetadata_getNativeMetadataPtr = nullptr;
+
+// Called at most once to initializes global variables used by JNI.
+bool InitJni(JNIEnv* env) {
+ // From C++11 onward, static initializers are guaranteed to be executed at most once,
+ // even if called from multiple threads.
+ static bool ok = [env]() -> bool {
+ const jclass cameraMetadataClazz = env->FindClass(
+ android_hardware_camera2_CameraMetadata_jniClassName);
+ if (cameraMetadataClazz == nullptr) {
+ return false;
+ }
+ const jmethodID cameraMetadata_getNativeMetadataPtr =
+ env->GetMethodID(cameraMetadataClazz, "getNativeMetadataPtr", "()J");
+ if (cameraMetadata_getNativeMetadataPtr == nullptr) {
+ return false;
+ }
+
+ const jclass cameraCharacteristics_clazz = env->FindClass(
+ android_hardware_camera2_CameraCharacteristics_jniClassName);
+ if (cameraCharacteristics_clazz == nullptr) {
+ return false;
+ }
+
+ const jclass captureResult_clazz = env->FindClass(
+ android_hardware_camera2_CaptureResult_jniClassName);
+ if (captureResult_clazz == nullptr) {
+ return false;
+ }
+
+ android_hardware_camera2_CameraMetadata_getNativeMetadataPtr =
+ cameraMetadata_getNativeMetadataPtr;
+ android_hardware_camera2_CameraCharacteristics_clazz =
+ static_cast<jclass>(env->NewGlobalRef(cameraCharacteristics_clazz));
+ android_hardware_camera2_CaptureResult_clazz =
+ static_cast<jclass>(env->NewGlobalRef(captureResult_clazz));
+
+ return true;
+ }();
+ return ok;
+}
+
+// Given cameraMetadata, an instance of android.hardware.camera2.CameraMetadata, invokes
+// cameraMetadata.getNativeMetadataPtr() and returns it as a CameraMetadata*.
+CameraMetadata* CameraMetadata_getNativeMetadataPtr(JNIEnv* env, jobject cameraMetadata) {
+ if (cameraMetadata == nullptr) {
+ ALOGE("%s: Invalid Java CameraMetadata object.", __FUNCTION__);
+ return nullptr;
+ }
+ jlong ret = env->CallLongMethod(cameraMetadata,
+ android_hardware_camera2_CameraMetadata_getNativeMetadataPtr);
+ return reinterpret_cast<CameraMetadata *>(ret);
+}
+
+} // namespace
+#endif /* __ANDROID_VNDK__ */
+
EXPORT
camera_status_t ACameraMetadata_getConstEntry(
const ACameraMetadata* acm, uint32_t tag, ACameraMetadata_const_entry* entry) {
@@ -58,7 +127,7 @@
return nullptr;
}
ACameraMetadata* copy = new ACameraMetadata(*src);
- copy->incStrong((void*) ACameraMetadata_copy);
+ copy->incStrong(/*id=*/(void*) ACameraMetadata_copy);
return copy;
}
@@ -86,3 +155,34 @@
return staticMetadata->isLogicalMultiCamera(numPhysicalCameras, physicalCameraIds);
}
+
+#ifndef __ANDROID_VNDK__
+EXPORT
+ACameraMetadata* ACameraMetadata_fromCameraMetadata(JNIEnv* env, jobject cameraMetadata) {
+ ATRACE_CALL();
+
+ const bool ok = InitJni(env);
+ LOG_ALWAYS_FATAL_IF(!ok, "Failed to find CameraMetadata Java classes.");
+
+ if (cameraMetadata == nullptr) {
+ return nullptr;
+ }
+
+ ACameraMetadata::ACAMERA_METADATA_TYPE type;
+ if (env->IsInstanceOf(cameraMetadata,
+ android_hardware_camera2_CameraCharacteristics_clazz)) {
+ type = ACameraMetadata::ACM_CHARACTERISTICS;
+ } else if (env->IsInstanceOf(cameraMetadata,
+ android_hardware_camera2_CaptureResult_clazz)) {
+ type = ACameraMetadata::ACM_RESULT;
+ } else {
+ return nullptr;
+ }
+
+ CameraMetadata* src = CameraMetadata_getNativeMetadataPtr(env,
+ cameraMetadata);
+ ACameraMetadata* output = new ACameraMetadata(src, type);
+ output->incStrong(/*id=*/(void*) ACameraMetadata_fromCameraMetadata);
+ return output;
+}
+#endif /* __ANDROID_VNDK__ */
\ No newline at end of file
diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp
index 46a8dae..0d7180a 100644
--- a/camera/ndk/impl/ACameraDevice.cpp
+++ b/camera/ndk/impl/ACameraDevice.cpp
@@ -710,7 +710,8 @@
if ((sessionParameters != nullptr) && (sessionParameters->settings != nullptr)) {
params.append(sessionParameters->settings->getInternalData());
}
- remoteRet = mRemote->endConfigure(/*isConstrainedHighSpeed*/ false, params);
+ std::vector<int> offlineStreamIds;
+ remoteRet = mRemote->endConfigure(/*isConstrainedHighSpeed*/ false, params, &offlineStreamIds);
if (remoteRet.serviceSpecificErrorCode() == hardware::ICameraService::ERROR_ILLEGAL_ARGUMENT) {
ALOGE("Camera device %s cannnot support app output configuration: %s", getId(),
remoteRet.toString8().string());
diff --git a/camera/ndk/impl/ACameraManager.cpp b/camera/ndk/impl/ACameraManager.cpp
index 7a0f63b..4870265 100644
--- a/camera/ndk/impl/ACameraManager.cpp
+++ b/camera/ndk/impl/ACameraManager.cpp
@@ -32,6 +32,7 @@
namespace acam {
// Static member definitions
const char* CameraManagerGlobal::kCameraIdKey = "CameraId";
+const char* CameraManagerGlobal::kPhysicalCameraIdKey = "PhysicalCameraId";
const char* CameraManagerGlobal::kCallbackFpKey = "CallbackFp";
const char* CameraManagerGlobal::kContextKey = "CallbackContext";
Mutex CameraManagerGlobal::sLock;
@@ -220,7 +221,7 @@
if (pair.second) {
for (auto& pair : mDeviceStatusMap) {
const String8& cameraId = pair.first;
- int32_t status = pair.second.status;
+ int32_t status = pair.second.getStatus();
// Don't send initial callbacks for camera ids which don't support
// camera2
if (!pair.second.supportsHAL3) {
@@ -264,9 +265,9 @@
// Needed to make sure we're connected to cameraservice
getCameraServiceLocked();
for(auto& deviceStatus : mDeviceStatusMap) {
- if (deviceStatus.second.status == hardware::ICameraServiceListener::STATUS_NOT_PRESENT ||
- deviceStatus.second.status ==
- hardware::ICameraServiceListener::STATUS_ENUMERATING) {
+ int32_t status = deviceStatus.second.getStatus();
+ if (status == hardware::ICameraServiceListener::STATUS_NOT_PRESENT ||
+ status == hardware::ICameraServiceListener::STATUS_ENUMERATING) {
continue;
}
if (!deviceStatus.second.supportsHAL3) {
@@ -341,6 +342,39 @@
(*cb)(context);
break;
}
+ case kWhatSendSinglePhysicalCameraCallback:
+ {
+ ACameraManager_PhysicalCameraAvailabilityCallback cb;
+ void* context;
+ AString cameraId;
+ AString physicalCameraId;
+ bool found = msg->findPointer(kCallbackFpKey, (void**) &cb);
+ if (!found) {
+ ALOGE("%s: Cannot find camera callback fp!", __FUNCTION__);
+ return;
+ }
+ if (cb == nullptr) {
+ // Physical camera callback is null
+ return;
+ }
+ found = msg->findPointer(kContextKey, &context);
+ if (!found) {
+ ALOGE("%s: Cannot find callback context!", __FUNCTION__);
+ return;
+ }
+ found = msg->findString(kCameraIdKey, &cameraId);
+ if (!found) {
+ ALOGE("%s: Cannot find camera ID!", __FUNCTION__);
+ return;
+ }
+ found = msg->findString(kPhysicalCameraIdKey, &physicalCameraId);
+ if (!found) {
+ ALOGE("%s: Cannot find physical camera ID!", __FUNCTION__);
+ return;
+ }
+ (*cb)(context, cameraId.c_str(), physicalCameraId.c_str());
+ break;
+ }
default:
ALOGE("%s: unknown message type %d", __FUNCTION__, msg->what());
break;
@@ -368,6 +402,17 @@
return binder::Status::ok();
}
+binder::Status CameraManagerGlobal::CameraServiceListener::onPhysicalCameraStatusChanged(
+ int32_t status, const String16& cameraId, const String16& physicalCameraId) {
+ sp<CameraManagerGlobal> cm = mCameraManager.promote();
+ if (cm != nullptr) {
+ cm->onStatusChanged(status, String8(cameraId), String8(physicalCameraId));
+ } else {
+ ALOGE("Cannot deliver physical camera status change. Global camera manager died");
+ }
+ return binder::Status::ok();
+}
+
void CameraManagerGlobal::onCameraAccessPrioritiesChanged() {
Mutex::Autolock _l(mLock);
for (auto cb : mCallbacks) {
@@ -397,7 +442,7 @@
bool firstStatus = (mDeviceStatusMap.count(cameraId) == 0);
int32_t oldStatus = firstStatus ?
status : // first status
- mDeviceStatusMap[cameraId].status;
+ mDeviceStatusMap[cameraId].getStatus();
if (!firstStatus &&
isStatusAvailable(status) == isStatusAvailable(oldStatus)) {
@@ -406,8 +451,14 @@
}
bool supportsHAL3 = supportsCamera2ApiLocked(cameraId);
+ if (firstStatus) {
+ mDeviceStatusMap.emplace(std::piecewise_construct,
+ std::forward_as_tuple(cameraId),
+ std::forward_as_tuple(status, supportsHAL3));
+ } else {
+ mDeviceStatusMap[cameraId].updateStatus(status);
+ }
// Iterate through all registered callbacks
- mDeviceStatusMap[cameraId] = StatusAndHAL3Support(status, supportsHAL3);
if (supportsHAL3) {
for (auto cb : mCallbacks) {
sp<AMessage> msg = new AMessage(kWhatSendSingleCallback, mHandler);
@@ -424,6 +475,81 @@
}
}
+void CameraManagerGlobal::onStatusChanged(
+ int32_t status, const String8& cameraId, const String8& physicalCameraId) {
+ Mutex::Autolock _l(mLock);
+ onStatusChangedLocked(status, cameraId, physicalCameraId);
+}
+
+void CameraManagerGlobal::onStatusChangedLocked(
+ int32_t status, const String8& cameraId, const String8& physicalCameraId) {
+ if (!validStatus(status)) {
+ ALOGE("%s: Invalid status %d", __FUNCTION__, status);
+ return;
+ }
+
+ auto logicalStatus = mDeviceStatusMap.find(cameraId);
+ if (logicalStatus == mDeviceStatusMap.end()) {
+ ALOGE("%s: Physical camera id %s status change on a non-present id %s",
+ __FUNCTION__, physicalCameraId.c_str(), cameraId.c_str());
+ return;
+ }
+ int32_t logicalCamStatus = mDeviceStatusMap[cameraId].getStatus();
+ if (logicalCamStatus != hardware::ICameraServiceListener::STATUS_PRESENT &&
+ logicalCamStatus != hardware::ICameraServiceListener::STATUS_NOT_AVAILABLE) {
+ ALOGE("%s: Physical camera id %s status %d change for an invalid logical camera state %d",
+ __FUNCTION__, physicalCameraId.string(), status, logicalCamStatus);
+ return;
+ }
+
+ bool supportsHAL3 = supportsCamera2ApiLocked(cameraId);
+
+ bool updated = false;
+ if (status == hardware::ICameraServiceListener::STATUS_PRESENT) {
+ updated = mDeviceStatusMap[cameraId].removeUnavailablePhysicalId(physicalCameraId);
+ } else {
+ updated = mDeviceStatusMap[cameraId].addUnavailablePhysicalId(physicalCameraId);
+ }
+
+ // Iterate through all registered callbacks
+ if (supportsHAL3 && updated) {
+ for (auto cb : mCallbacks) {
+ sp<AMessage> msg = new AMessage(kWhatSendSinglePhysicalCameraCallback, mHandler);
+ ACameraManager_PhysicalCameraAvailabilityCallback cbFp = isStatusAvailable(status) ?
+ cb.mPhysicalCamAvailable : cb.mPhysicalCamUnavailable;
+ msg->setPointer(kCallbackFpKey, (void *) cbFp);
+ msg->setPointer(kContextKey, cb.mContext);
+ msg->setString(kCameraIdKey, AString(cameraId));
+ msg->setString(kPhysicalCameraIdKey, AString(physicalCameraId));
+ msg->post();
+ }
+ }
+}
+
+int32_t CameraManagerGlobal::StatusAndHAL3Support::getStatus() {
+ std::lock_guard<std::mutex> lock(mLock);
+ return status;
+}
+
+void CameraManagerGlobal::StatusAndHAL3Support::updateStatus(int32_t newStatus) {
+ std::lock_guard<std::mutex> lock(mLock);
+ status = newStatus;
+}
+
+bool CameraManagerGlobal::StatusAndHAL3Support::addUnavailablePhysicalId(
+ const String8& physicalCameraId) {
+ std::lock_guard<std::mutex> lock(mLock);
+ auto result = unavailablePhysicalIds.insert(physicalCameraId);
+ return result.second;
+}
+
+bool CameraManagerGlobal::StatusAndHAL3Support::removeUnavailablePhysicalId(
+ const String8& physicalCameraId) {
+ std::lock_guard<std::mutex> lock(mLock);
+ auto count = unavailablePhysicalIds.erase(physicalCameraId);
+ return count > 0;
+}
+
} // namespace acam
} // namespace android
diff --git a/camera/ndk/impl/ACameraManager.h b/camera/ndk/impl/ACameraManager.h
index e945ba0..98cd934 100644
--- a/camera/ndk/impl/ACameraManager.h
+++ b/camera/ndk/impl/ACameraManager.h
@@ -85,6 +85,8 @@
public:
explicit CameraServiceListener(CameraManagerGlobal* cm) : mCameraManager(cm) {}
virtual binder::Status onStatusChanged(int32_t status, const String16& cameraId);
+ virtual binder::Status onPhysicalCameraStatusChanged(int32_t status,
+ const String16& cameraId, const String16& physicalCameraId);
// Torch API not implemented yet
virtual binder::Status onTorchStatusChanged(int32_t, const String16&) {
@@ -104,18 +106,24 @@
mAvailable(callback->onCameraAvailable),
mUnavailable(callback->onCameraUnavailable),
mAccessPriorityChanged(nullptr),
+ mPhysicalCamAvailable(nullptr),
+ mPhysicalCamUnavailable(nullptr),
mContext(callback->context) {}
explicit Callback(const ACameraManager_ExtendedAvailabilityCallbacks *callback) :
mAvailable(callback->availabilityCallbacks.onCameraAvailable),
mUnavailable(callback->availabilityCallbacks.onCameraUnavailable),
mAccessPriorityChanged(callback->onCameraAccessPrioritiesChanged),
+ mPhysicalCamAvailable(callback->onPhysicalCameraAvailable),
+ mPhysicalCamUnavailable(callback->onPhysicalCameraUnavailable),
mContext(callback->availabilityCallbacks.context) {}
bool operator == (const Callback& other) const {
return (mAvailable == other.mAvailable &&
mUnavailable == other.mUnavailable &&
mAccessPriorityChanged == other.mAccessPriorityChanged &&
+ mPhysicalCamAvailable == other.mPhysicalCamAvailable &&
+ mPhysicalCamUnavailable == other.mPhysicalCamUnavailable &&
mContext == other.mContext);
}
bool operator != (const Callback& other) const {
@@ -124,6 +132,12 @@
bool operator < (const Callback& other) const {
if (*this == other) return false;
if (mContext != other.mContext) return mContext < other.mContext;
+ if (mPhysicalCamAvailable != other.mPhysicalCamAvailable) {
+ return mPhysicalCamAvailable < other.mPhysicalCamAvailable;
+ }
+ if (mPhysicalCamUnavailable != other.mPhysicalCamUnavailable) {
+ return mPhysicalCamUnavailable < other.mPhysicalCamUnavailable;
+ }
if (mAccessPriorityChanged != other.mAccessPriorityChanged) {
return mAccessPriorityChanged < other.mAccessPriorityChanged;
}
@@ -136,6 +150,8 @@
ACameraManager_AvailabilityCallback mAvailable;
ACameraManager_AvailabilityCallback mUnavailable;
ACameraManager_AccessPrioritiesChangedCallback mAccessPriorityChanged;
+ ACameraManager_PhysicalCameraAvailabilityCallback mPhysicalCamAvailable;
+ ACameraManager_PhysicalCameraAvailabilityCallback mPhysicalCamUnavailable;
void* mContext;
};
std::set<Callback> mCallbacks;
@@ -144,8 +160,10 @@
enum {
kWhatSendSingleCallback,
kWhatSendSingleAccessCallback,
+ kWhatSendSinglePhysicalCameraCallback,
};
static const char* kCameraIdKey;
+ static const char* kPhysicalCameraIdKey;
static const char* kCallbackFpKey;
static const char* kContextKey;
class CallbackHandler : public AHandler {
@@ -160,6 +178,9 @@
void onCameraAccessPrioritiesChanged();
void onStatusChanged(int32_t status, const String8& cameraId);
void onStatusChangedLocked(int32_t status, const String8& cameraId);
+ void onStatusChanged(int32_t status, const String8& cameraId, const String8& physicalCameraId);
+ void onStatusChangedLocked(int32_t status, const String8& cameraId,
+ const String8& physicalCameraId);
// Utils for status
static bool validStatus(int32_t status);
static bool isStatusAvailable(int32_t status);
@@ -187,11 +208,20 @@
};
struct StatusAndHAL3Support {
+ private:
int32_t status = hardware::ICameraServiceListener::STATUS_NOT_PRESENT;
- bool supportsHAL3 = false;
+ mutable std::mutex mLock;
+ std::set<String8> unavailablePhysicalIds;
+ public:
+ const bool supportsHAL3 = false;
StatusAndHAL3Support(int32_t st, bool HAL3support):
status(st), supportsHAL3(HAL3support) { };
StatusAndHAL3Support() = default;
+
+ bool addUnavailablePhysicalId(const String8& physicalCameraId);
+ bool removeUnavailablePhysicalId(const String8& physicalCameraId);
+ int32_t getStatus();
+ void updateStatus(int32_t newStatus);
};
// Map camera_id -> status
diff --git a/camera/ndk/impl/ACameraMetadata.cpp b/camera/ndk/impl/ACameraMetadata.cpp
index 7c41b5e..18c5d3c 100644
--- a/camera/ndk/impl/ACameraMetadata.cpp
+++ b/camera/ndk/impl/ACameraMetadata.cpp
@@ -28,7 +28,37 @@
* ACameraMetadata Implementation
*/
ACameraMetadata::ACameraMetadata(camera_metadata_t* buffer, ACAMERA_METADATA_TYPE type) :
- mData(buffer), mType(type) {
+ mData(new CameraMetadata(buffer)),
+ mOwnsData(true),
+ mType(type) {
+ init();
+}
+
+ACameraMetadata::ACameraMetadata(CameraMetadata* cameraMetadata, ACAMERA_METADATA_TYPE type) :
+ mData(cameraMetadata),
+ mOwnsData(false),
+ mType(type) {
+ init();
+}
+
+ACameraMetadata::ACameraMetadata(const ACameraMetadata& other) :
+ mOwnsData(other.mOwnsData),
+ mType(other.mType) {
+ if (other.mOwnsData) {
+ mData = new CameraMetadata(*(other.mData));
+ } else {
+ mData = other.mData;
+ }
+}
+
+ACameraMetadata::~ACameraMetadata() {
+ if (mOwnsData) {
+ delete mData;
+ }
+}
+
+void
+ACameraMetadata::init() {
if (mType == ACM_CHARACTERISTICS) {
filterUnsupportedFeatures();
filterStreamConfigurations();
@@ -61,7 +91,7 @@
void
ACameraMetadata::filterUnsupportedFeatures() {
// Hide unsupported capabilities (reprocessing)
- camera_metadata_entry entry = mData.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
+ camera_metadata_entry entry = mData->find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
if (entry.count == 0 || entry.type != TYPE_BYTE) {
ALOGE("%s: malformed available capability key! count %zu, type %d",
__FUNCTION__, entry.count, entry.type);
@@ -80,7 +110,7 @@
}
}
}
- mData.update(ANDROID_REQUEST_AVAILABLE_CAPABILITIES, capabilities);
+ mData->update(ANDROID_REQUEST_AVAILABLE_CAPABILITIES, capabilities);
}
void
@@ -118,7 +148,7 @@
const int STREAM_WIDTH_OFFSET = 1;
const int STREAM_HEIGHT_OFFSET = 2;
const int STREAM_DURATION_OFFSET = 3;
- camera_metadata_entry entry = mData.find(tag);
+ camera_metadata_entry entry = mData->find(tag);
if (entry.count == 0 || entry.count % 4 || entry.type != TYPE_INT64) {
ALOGE("%s: malformed duration key %d! count %zu, type %d",
__FUNCTION__, tag, entry.count, entry.type);
@@ -194,7 +224,7 @@
}
}
- mData.update(tag, filteredDurations);
+ mData->update(tag, filteredDurations);
}
void
@@ -204,7 +234,7 @@
const int STREAM_WIDTH_OFFSET = 1;
const int STREAM_HEIGHT_OFFSET = 2;
const int STREAM_IS_INPUT_OFFSET = 3;
- camera_metadata_entry entry = mData.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
+ camera_metadata_entry entry = mData->find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
if (entry.count > 0 && (entry.count % 4 || entry.type != TYPE_INT32)) {
ALOGE("%s: malformed available stream configuration key! count %zu, type %d",
__FUNCTION__, entry.count, entry.type);
@@ -234,10 +264,10 @@
}
if (filteredStreamConfigs.size() > 0) {
- mData.update(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, filteredStreamConfigs);
+ mData->update(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, filteredStreamConfigs);
}
- entry = mData.find(ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS);
+ entry = mData->find(ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS);
if (entry.count > 0 && (entry.count % 4 || entry.type != TYPE_INT32)) {
ALOGE("%s: malformed available depth stream configuration key! count %zu, type %d",
__FUNCTION__, entry.count, entry.type);
@@ -270,11 +300,11 @@
}
if (filteredDepthStreamConfigs.size() > 0) {
- mData.update(ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS,
+ mData->update(ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS,
filteredDepthStreamConfigs);
}
- entry = mData.find(ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS);
+ entry = mData->find(ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS);
Vector<int32_t> filteredHeicStreamConfigs;
filteredHeicStreamConfigs.setCapacity(entry.count);
@@ -297,9 +327,9 @@
filteredHeicStreamConfigs.push_back(height);
filteredHeicStreamConfigs.push_back(isInput);
}
- mData.update(ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS, filteredHeicStreamConfigs);
+ mData->update(ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS, filteredHeicStreamConfigs);
- entry = mData.find(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS);
+ entry = mData->find(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS);
Vector<int32_t> filteredDynamicDepthStreamConfigs;
filteredDynamicDepthStreamConfigs.setCapacity(entry.count);
@@ -322,7 +352,7 @@
filteredDynamicDepthStreamConfigs.push_back(height);
filteredDynamicDepthStreamConfigs.push_back(isInput);
}
- mData.update(ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS,
+ mData->update(ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS,
filteredDynamicDepthStreamConfigs);
}
@@ -343,7 +373,7 @@
Mutex::Autolock _l(mLock);
- camera_metadata_ro_entry rawEntry = mData.find(tag);
+ camera_metadata_ro_entry rawEntry = static_cast<const CameraMetadata*>(mData)->find(tag);
if (rawEntry.count == 0) {
ALOGE("%s: cannot find metadata tag %d", __FUNCTION__, tag);
return ACAMERA_ERROR_METADATA_NOT_FOUND;
@@ -390,9 +420,9 @@
/*out*/const uint32_t** tags) const {
Mutex::Autolock _l(mLock);
if (mTags.size() == 0) {
- size_t entry_count = mData.entryCount();
+ size_t entry_count = mData->entryCount();
mTags.setCapacity(entry_count);
- const camera_metadata_t* rawMetadata = mData.getAndLock();
+ const camera_metadata_t* rawMetadata = mData->getAndLock();
for (size_t i = 0; i < entry_count; i++) {
camera_metadata_ro_entry_t entry;
int ret = get_camera_metadata_ro_entry(rawMetadata, i, &entry);
@@ -405,7 +435,7 @@
mTags.push_back(entry.tag);
}
}
- mData.unlock(rawMetadata);
+ mData->unlock(rawMetadata);
}
*numTags = mTags.size();
@@ -415,7 +445,7 @@
const CameraMetadata&
ACameraMetadata::getInternalData() const {
- return mData;
+ return (*mData);
}
bool
@@ -480,6 +510,7 @@
case ACAMERA_CONTROL_POST_RAW_SENSITIVITY_BOOST:
case ACAMERA_CONTROL_ENABLE_ZSL:
case ACAMERA_CONTROL_BOKEH_MODE:
+ case ACAMERA_CONTROL_ZOOM_RATIO:
case ACAMERA_EDGE_MODE:
case ACAMERA_FLASH_MODE:
case ACAMERA_HOT_PIXEL_MODE:
@@ -497,6 +528,7 @@
case ACAMERA_LENS_OPTICAL_STABILIZATION_MODE:
case ACAMERA_NOISE_REDUCTION_MODE:
case ACAMERA_SCALER_CROP_REGION:
+ case ACAMERA_SCALER_ROTATE_AND_CROP:
case ACAMERA_SENSOR_EXPOSURE_TIME:
case ACAMERA_SENSOR_FRAME_DURATION:
case ACAMERA_SENSOR_SENSITIVITY:
diff --git a/camera/ndk/impl/ACameraMetadata.h b/camera/ndk/impl/ACameraMetadata.h
index 97f7f48..a57b2ff 100644
--- a/camera/ndk/impl/ACameraMetadata.h
+++ b/camera/ndk/impl/ACameraMetadata.h
@@ -36,8 +36,8 @@
using namespace android;
/**
- * ACameraMetadata opaque struct definition
- * Leave outside of android namespace because it's NDK struct
+ * ACameraMetadata is an opaque struct definition.
+ * It is intentionally left outside of the android namespace because it's NDK struct.
*/
struct ACameraMetadata : public RefBase {
public:
@@ -47,11 +47,20 @@
ACM_RESULT, // Read only
} ACAMERA_METADATA_TYPE;
- // Takes ownership of pass-in buffer
- ACameraMetadata(camera_metadata_t *buffer, ACAMERA_METADATA_TYPE type);
- // Clone
- ACameraMetadata(const ACameraMetadata& other) :
- mData(other.mData), mType(other.mType) {};
+ // Constructs a ACameraMetadata that takes ownership of `buffer`.
+ ACameraMetadata(camera_metadata_t* buffer, ACAMERA_METADATA_TYPE type);
+
+ // Constructs a ACameraMetadata that is a view of `cameraMetadata`.
+ // `cameraMetadata` will not be deleted by ~ACameraMetadata().
+ ACameraMetadata(CameraMetadata* cameraMetadata, ACAMERA_METADATA_TYPE type);
+
+ // Copy constructor.
+ //
+ // If `other` owns its CameraMetadata, then makes a deep copy.
+ // Otherwise, the new instance is also a view of the same data.
+ ACameraMetadata(const ACameraMetadata& other);
+
+ ~ACameraMetadata();
camera_status_t getConstEntry(uint32_t tag, ACameraMetadata_const_entry* entry) const;
@@ -70,6 +79,9 @@
private:
+ // Common code called by constructors.
+ void init();
+
// This function does not check whether the capability passed to it is valid.
// The caller must make sure that it is.
bool isNdkSupportedCapability(const int32_t capability);
@@ -95,11 +107,11 @@
status_t ret = OK;
if (count == 0 && data == nullptr) {
- ret = mData.erase(tag);
+ ret = mData->erase(tag);
} else {
// Here we have to use reinterpret_cast because the NDK data type is
// exact copy of internal data type but they do not inherit from each other
- ret = mData.update(tag, reinterpret_cast<const INTERNAL_T*>(data), count);
+ ret = mData->update(tag, reinterpret_cast<const INTERNAL_T*>(data), count);
}
if (ret == OK) {
@@ -110,10 +122,14 @@
}
}
- // guard access of public APIs: get/update/getTags
- mutable Mutex mLock;
- CameraMetadata mData;
- mutable Vector<uint32_t> mTags; // updated in getTags, cleared by update
+ // Guard access of public APIs: get/update/getTags.
+ mutable Mutex mLock;
+
+ CameraMetadata* mData;
+ // If true, has ownership of mData. Otherwise, mData is a view of an external instance.
+ bool mOwnsData;
+
+ mutable Vector<uint32_t> mTags; // Updated by `getTags()`, cleared by `update()`.
const ACAMERA_METADATA_TYPE mType;
static std::unordered_set<uint32_t> sSystemTags;
diff --git a/camera/ndk/include/camera/NdkCameraManager.h b/camera/ndk/include/camera/NdkCameraManager.h
index 2cc8a97..e2b71bf 100644
--- a/camera/ndk/include/camera/NdkCameraManager.h
+++ b/camera/ndk/include/camera/NdkCameraManager.h
@@ -123,6 +123,21 @@
const char* cameraId);
/**
+ * Definition of physical camera availability callbacks.
+ *
+ * @param context The optional application context provided by user in
+ * {@link ACameraManager_AvailabilityCallbacks}.
+ * @param cameraId The ID of the logical multi-camera device whose physical camera status is
+ * changing. The memory of this argument is owned by camera framework and will
+ * become invalid immediately after this callback returns.
+ * @param physicalCameraId The ID of the physical camera device whose status is changing. The
+ * memory of this argument is owned by camera framework and will become invalid
+ * immediately after this callback returns.
+ */
+typedef void (*ACameraManager_PhysicalCameraAvailabilityCallback)(void* context,
+ const char* cameraId, const char* physicalCameraId);
+
+/**
* A listener for camera devices becoming available or unavailable to open.
*
* <p>Cameras become available when they are no longer in use, or when a new
@@ -320,8 +335,15 @@
/// Called when there is camera access permission change
ACameraManager_AccessPrioritiesChangedCallback onCameraAccessPrioritiesChanged;
+ /// Called when a physical camera becomes available
+ ACameraManager_PhysicalCameraAvailabilityCallback onPhysicalCameraAvailable __INTRODUCED_IN(30);
+
+ /// Called when a physical camera becomes unavailable
+ ACameraManager_PhysicalCameraAvailabilityCallback onPhysicalCameraUnavailable
+ __INTRODUCED_IN(30);
+
/// Reserved for future use, please ensure that all entries are set to NULL
- void *reserved[6];
+ void *reserved[4];
} ACameraManager_ExtendedAvailabilityCallbacks;
/**
diff --git a/camera/ndk/include/camera/NdkCameraMetadata.h b/camera/ndk/include/camera/NdkCameraMetadata.h
index 9bbfb83..ee75610 100644
--- a/camera/ndk/include/camera/NdkCameraMetadata.h
+++ b/camera/ndk/include/camera/NdkCameraMetadata.h
@@ -39,6 +39,12 @@
#include <stdint.h>
#include <sys/cdefs.h>
+#ifndef __ANDROID_VNDK__
+#if __ANDROID_API__ >= 30
+#include "jni.h"
+#endif /* __ANDROID_API__ >= 30 */
+#endif /* __ANDROID_VNDK__ */
+
#include "NdkCameraError.h"
#include "NdkCameraMetadataTags.h"
@@ -255,8 +261,40 @@
#endif /* __ANDROID_API__ >= 29 */
+#ifndef __ANDROID_VNDK__
+#if __ANDROID_API__ >= 30
+
+/**
+ * Return a {@link ACameraMetadata} that references the same data as
+ * {@link cameraMetadata}, which is an instance of
+ * {@link android.hardware.camera2.CameraMetadata} (e.g., a
+ * {@link android.hardware.camera2.CameraCharacteristics} or
+ * {@link android.hardware.camera2.CaptureResult}).
+ *
+ * <p>The returned ACameraMetadata must be freed by the application by {@link ACameraMetadata_free}
+ * after application is done using it.</p>
+ *
+ * <p>This function does not affect the lifetime of {@link cameraMetadata}. Attempting to use the
+ * returned ACameraMetadata object after {@link cameraMetadata} has been garbage collected is
+ * unsafe. To manage the lifetime beyond the current JNI function call, use
+ * {@code env->NewGlobalRef()} and {@code env->DeleteGlobalRef()}.
+ *
+ * @param env the JNI environment.
+ * @param cameraMetadata the source {@link android.hardware.camera2.CameraMetadata} from which the
+ * returned {@link ACameraMetadata} is a view.
+ *
+ * @return a valid ACameraMetadata pointer or NULL if {@link cameraMetadata} is null or not a valid
+ * instance of {@link android.hardware.camera2.CameraMetadata}.
+ *
+ */
+ACameraMetadata* ACameraMetadata_fromCameraMetadata(JNIEnv* env, jobject cameraMetadata)
+ __INTRODUCED_IN(30);
+
+#endif /* __ANDROID_API__ >= 30 */
+#endif /* __ANDROID_VNDK__ */
+
__END_DECLS
#endif /* _NDK_CAMERA_METADATA_H */
-/** @} */
+/** @} */
\ No newline at end of file
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index 825f308..bd259eb 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -518,11 +518,22 @@
* region and output only the intersection rectangle as the metering region in the result
* metadata. If the region is entirely outside the crop region, it will be ignored and
* not reported in the result metadata.</p>
+ * <p>Starting from API level 30, the coordinate system of activeArraySize or
+ * preCorrectionActiveArraySize is used to represent post-zoomRatio field of view, not
+ * pre-zoom field of view. This means that the same aeRegions values at different
+ * ACAMERA_CONTROL_ZOOM_RATIO represent different parts of the scene. The aeRegions
+ * coordinates are relative to the activeArray/preCorrectionActiveArray representing the
+ * zoomed field of view. If ACAMERA_CONTROL_ZOOM_RATIO is set to 1.0 (default), the same
+ * aeRegions at different ACAMERA_SCALER_CROP_REGION still represent the same parts of the
+ * scene as they do before. See ACAMERA_CONTROL_ZOOM_RATIO for details. Whether to use
+ * activeArraySize or preCorrectionActiveArraySize still depends on distortion correction
+ * mode.</p>
* <p>The data representation is <code>int[5 * area_count]</code>.
* Every five elements represent a metering region of <code>(xmin, ymin, xmax, ymax, weight)</code>.
* The rectangle is defined to be inclusive on xmin and ymin, but exclusive on xmax and
* ymax.</p>
*
+ * @see ACAMERA_CONTROL_ZOOM_RATIO
* @see ACAMERA_DISTORTION_CORRECTION_MODE
* @see ACAMERA_SCALER_CROP_REGION
* @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
@@ -698,11 +709,22 @@
* region and output only the intersection rectangle as the metering region in the result
* metadata. If the region is entirely outside the crop region, it will be ignored and
* not reported in the result metadata.</p>
+ * <p>Starting from API level 30, the coordinate system of activeArraySize or
+ * preCorrectionActiveArraySize is used to represent post-zoomRatio field of view, not
+ * pre-zoom field of view. This means that the same afRegions values at different
+ * ACAMERA_CONTROL_ZOOM_RATIO represent different parts of the scene. The afRegions
+ * coordinates are relative to the activeArray/preCorrectionActiveArray representing the
+ * zoomed field of view. If ACAMERA_CONTROL_ZOOM_RATIO is set to 1.0 (default), the same
+ * afRegions at different ACAMERA_SCALER_CROP_REGION still represent the same parts of the
+ * scene as they do before. See ACAMERA_CONTROL_ZOOM_RATIO for details. Whether to use
+ * activeArraySize or preCorrectionActiveArraySize still depends on distortion correction
+ * mode.</p>
* <p>The data representation is <code>int[5 * area_count]</code>.
* Every five elements represent a metering region of <code>(xmin, ymin, xmax, ymax, weight)</code>.
* The rectangle is defined to be inclusive on xmin and ymin, but exclusive on xmax and
* ymax.</p>
*
+ * @see ACAMERA_CONTROL_ZOOM_RATIO
* @see ACAMERA_DISTORTION_CORRECTION_MODE
* @see ACAMERA_SCALER_CROP_REGION
* @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
@@ -873,11 +895,22 @@
* region and output only the intersection rectangle as the metering region in the result
* metadata. If the region is entirely outside the crop region, it will be ignored and
* not reported in the result metadata.</p>
+ * <p>Starting from API level 30, the coordinate system of activeArraySize or
+ * preCorrectionActiveArraySize is used to represent post-zoomRatio field of view, not
+ * pre-zoom field of view. This means that the same awbRegions values at different
+ * ACAMERA_CONTROL_ZOOM_RATIO represent different parts of the scene. The awbRegions
+ * coordinates are relative to the activeArray/preCorrectionActiveArray representing the
+ * zoomed field of view. If ACAMERA_CONTROL_ZOOM_RATIO is set to 1.0 (default), the same
+ * awbRegions at different ACAMERA_SCALER_CROP_REGION still represent the same parts of
+ * the scene as they do before. See ACAMERA_CONTROL_ZOOM_RATIO for details. Whether to use
+ * activeArraySize or preCorrectionActiveArraySize still depends on distortion correction
+ * mode.</p>
* <p>The data representation is <code>int[5 * area_count]</code>.
* Every five elements represent a metering region of <code>(xmin, ymin, xmax, ymax, weight)</code>.
* The rectangle is defined to be inclusive on xmin and ymin, but exclusive on xmax and
* ymax.</p>
*
+ * @see ACAMERA_CONTROL_ZOOM_RATIO
* @see ACAMERA_DISTORTION_CORRECTION_MODE
* @see ACAMERA_SCALER_CROP_REGION
* @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
@@ -1311,7 +1344,7 @@
/**
* <p>List of the maximum number of regions that can be used for metering in
* auto-exposure (AE), auto-white balance (AWB), and auto-focus (AF);
- * this corresponds to the the maximum number of elements in
+ * this corresponds to the maximum number of elements in
* ACAMERA_CONTROL_AE_REGIONS, ACAMERA_CONTROL_AWB_REGIONS,
* and ACAMERA_CONTROL_AF_REGIONS.</p>
*
@@ -1735,8 +1768,10 @@
ACAMERA_CONTROL_AF_SCENE_CHANGE = // byte (acamera_metadata_enum_android_control_af_scene_change_t)
ACAMERA_CONTROL_START + 42,
/**
- * <p>The list of bokeh modes that are supported by this camera device, and each bokeh mode's
- * maximum streaming (non-stall) size with bokeh effect.</p>
+ * <p>The list of bokeh modes for ACAMERA_CONTROL_BOKEH_MODE that are supported by this camera
+ * device, and each bokeh mode's maximum streaming (non-stall) size with bokeh effect.</p>
+ *
+ * @see ACAMERA_CONTROL_BOKEH_MODE
*
* <p>Type: int32[3*n]</p>
*
@@ -1761,9 +1796,31 @@
* application configures a stream with larger dimension, the stream may not have bokeh
* effect applied.</p>
*/
- ACAMERA_CONTROL_AVAILABLE_BOKEH_CAPABILITIES = // int32[3*n]
+ ACAMERA_CONTROL_AVAILABLE_BOKEH_MAX_SIZES = // int32[3*n]
ACAMERA_CONTROL_START + 43,
/**
+ * <p>The ranges of supported zoom ratio for non-OFF ACAMERA_CONTROL_BOKEH_MODE.</p>
+ *
+ * @see ACAMERA_CONTROL_BOKEH_MODE
+ *
+ * <p>Type: float[2*n]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>When bokeh mode is enabled, the camera device may have limited range of zoom ratios
+ * compared to when bokeh mode is disabled. This tag lists the zoom ratio ranges for all
+ * supported non-OFF bokeh modes, in the same order as in
+ * ACAMERA_CONTROL_AVAILABLE_BOKEH_CAPABILITIES.</p>
+ * <p>Range [1.0, 1.0] means that no zoom (optical or digital) is supported.</p>
+ *
+ * @see ACAMERA_CONTROL_AVAILABLE_BOKEH_CAPABILITIES
+ */
+ ACAMERA_CONTROL_AVAILABLE_BOKEH_ZOOM_RATIO_RANGES = // float[2*n]
+ ACAMERA_CONTROL_START + 44,
+ /**
* <p>Whether bokeh mode is enabled for a particular capture request.</p>
*
* <p>Type: byte (acamera_metadata_enum_android_control_bokeh_mode_t)</p>
@@ -1800,11 +1857,79 @@
* stream combinations of LIMITED hardware level are guaranteed.</p>
* <p>For a logical multi-camera, bokeh may be implemented by stereo vision from sub-cameras
* with different field of view. As a result, when bokeh mode is enabled, the camera device
- * may override android.scaler.CropRegion, and the field of view will be smaller than when
+ * may override ACAMERA_SCALER_CROP_REGION, and the field of view will be smaller than when
* bokeh mode is off.</p>
+ *
+ * @see ACAMERA_SCALER_CROP_REGION
*/
ACAMERA_CONTROL_BOKEH_MODE = // byte (acamera_metadata_enum_android_control_bokeh_mode_t)
- ACAMERA_CONTROL_START + 44,
+ ACAMERA_CONTROL_START + 45,
+ /**
+ * <p>Minimum and maximum zoom ratios supported by this camera device.</p>
+ *
+ * <p>Type: float[2]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>If the camera device supports zoom-out from 1x zoom, minZoom will be less than 1.0, and
+ * setting ACAMERA_CONTROL_ZOOM_RATIO to values less than 1.0 increases the camera's field
+ * of view.</p>
+ *
+ * @see ACAMERA_CONTROL_ZOOM_RATIO
+ */
+ ACAMERA_CONTROL_ZOOM_RATIO_RANGE = // float[2]
+ ACAMERA_CONTROL_START + 46,
+ /**
+ * <p>The desired zoom ratio</p>
+ *
+ * <p>Type: float</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
+ * <li>ACaptureRequest</li>
+ * </ul></p>
+ *
+ * <p>Instead of using ACAMERA_SCALER_CROP_REGION with dual purposes of crop and zoom, the
+ * application can now choose to use this tag to specify the desired zoom level. The
+ * ACAMERA_SCALER_CROP_REGION can still be used to specify the horizontal or vertical
+ * crop to achieve aspect ratios different than the native camera sensor.</p>
+ * <p>By using this control, the application gains a simpler way to control zoom, which can
+ * be a combination of optical and digital zoom. More specifically, for a logical
+ * multi-camera with more than one focal length, using a floating point zoom ratio offers
+ * more zoom precision when a telephoto lens is used, as well as allowing zoom ratio of
+ * less than 1.0 to zoom out to a wide field of view.</p>
+ * <p>Note that the coordinate system of cropRegion, AE/AWB/AF regions, and faces now changes
+ * to the effective after-zoom field-of-view represented by rectangle of (0, 0,
+ * activeArrayWidth, activeArrayHeight).</p>
+ * <p>For example, if ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE is 4032*3024, and the preview stream
+ * is configured to the same 4:3 aspect ratio, the application can achieve 2.0x zoom in
+ * one of two ways:</p>
+ * <ul>
+ * <li>zoomRatio = 2.0, scaler.cropRegion = (0, 0, 4032, 3024)</li>
+ * <li>zoomRatio = 1.0 (default), scaler.cropRegion = (1008, 756, 3024, 2268)</li>
+ * </ul>
+ * <p>If the application intends to set aeRegions to be top-left quarter of the preview
+ * field-of-view, the ACAMERA_CONTROL_AE_REGIONS should be set to (0, 0, 2016, 1512) with
+ * zoomRatio set to 2.0. Alternatively, the application can set aeRegions to the equivalent
+ * region of (1008, 756, 2016, 1512) for zoomRatio of 1.0. If the application doesn't
+ * explicitly set ACAMERA_CONTROL_ZOOM_RATIO, its value defaults to 1.0.</p>
+ * <p>This coordinate system change isn't applicable to RAW capture and its related metadata
+ * such as intrinsicCalibration and lensShadingMap.</p>
+ * <p>One limitation of controlling zoom using zoomRatio is that the ACAMERA_SCALER_CROP_REGION
+ * must only be used for letterboxing or pillarboxing of the sensor active array, and no
+ * FREEFORM cropping can be used with ACAMERA_CONTROL_ZOOM_RATIO other than 1.0.</p>
+ *
+ * @see ACAMERA_CONTROL_AE_REGIONS
+ * @see ACAMERA_CONTROL_ZOOM_RATIO
+ * @see ACAMERA_SCALER_CROP_REGION
+ * @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ */
+ ACAMERA_CONTROL_ZOOM_RATIO = // float
+ ACAMERA_CONTROL_START + 47,
ACAMERA_CONTROL_END,
/**
@@ -2384,6 +2509,11 @@
* <p><code>p' = Rp</code></p>
* <p>where <code>p</code> is in the device sensor coordinate system, and
* <code>p'</code> is in the camera-oriented coordinate system.</p>
+ * <p>If ACAMERA_LENS_POSE_REFERENCE is UNDEFINED, the quaternion rotation cannot
+ * be accurately represented by the camera device, and will be represented by
+ * default values matching its default facing.</p>
+ *
+ * @see ACAMERA_LENS_POSE_REFERENCE
*/
ACAMERA_LENS_POSE_ROTATION = // float[4]
ACAMERA_LENS_START + 6,
@@ -2424,6 +2554,8 @@
* <p>When ACAMERA_LENS_POSE_REFERENCE is GYROSCOPE, then this position is relative to
* the center of the primary gyroscope on the device. The axis definitions are the same as
* with PRIMARY_CAMERA.</p>
+ * <p>When ACAMERA_LENS_POSE_REFERENCE is UNDEFINED, this position cannot be accurately
+ * represented by the camera device, and will be represented as <code>(0, 0, 0)</code>.</p>
*
* @see ACAMERA_LENS_DISTORTION
* @see ACAMERA_LENS_INTRINSIC_CALIBRATION
@@ -2566,8 +2698,10 @@
ACAMERA_LENS_RADIAL_DISTORTION = // Deprecated! DO NOT USE
ACAMERA_LENS_START + 11,
/**
- * <p>The origin for ACAMERA_LENS_POSE_TRANSLATION.</p>
+ * <p>The origin for ACAMERA_LENS_POSE_TRANSLATION, and the accuracy of
+ * ACAMERA_LENS_POSE_TRANSLATION and ACAMERA_LENS_POSE_ROTATION.</p>
*
+ * @see ACAMERA_LENS_POSE_ROTATION
* @see ACAMERA_LENS_POSE_TRANSLATION
*
* <p>Type: byte (acamera_metadata_enum_android_lens_pose_reference_t)</p>
@@ -3163,32 +3297,70 @@
* <p>For devices not supporting ACAMERA_DISTORTION_CORRECTION_MODE control, the coordinate
* system always follows that of ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE, with <code>(0, 0)</code> being
* the top-left pixel of the active array.</p>
- * <p>For devices supporting ACAMERA_DISTORTION_CORRECTION_MODE control, the coordinate
- * system depends on the mode being set.
- * When the distortion correction mode is OFF, the coordinate system follows
- * ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, with
- * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array.
- * When the distortion correction mode is not OFF, the coordinate system follows
- * ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE, with
- * <code>(0, 0)</code> being the top-left pixel of the active array.</p>
- * <p>Output streams use this rectangle to produce their output,
- * cropping to a smaller region if necessary to maintain the
- * stream's aspect ratio, then scaling the sensor input to
+ * <p>For devices supporting ACAMERA_DISTORTION_CORRECTION_MODE control, the coordinate system
+ * depends on the mode being set. When the distortion correction mode is OFF, the
+ * coordinate system follows ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, with <code>(0,
+ * 0)</code> being the top-left pixel of the pre-correction active array. When the distortion
+ * correction mode is not OFF, the coordinate system follows
+ * ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE, with <code>(0, 0)</code> being the top-left pixel of the
+ * active array.</p>
+ * <p>Output streams use this rectangle to produce their output, cropping to a smaller region
+ * if necessary to maintain the stream's aspect ratio, then scaling the sensor input to
* match the output's configured resolution.</p>
- * <p>The crop region is applied after the RAW to other color
- * space (e.g. YUV) conversion. Since raw streams
- * (e.g. RAW16) don't have the conversion stage, they are not
+ * <p>The crop region is applied after the RAW to other color space (e.g. YUV)
+ * conversion. Since raw streams (e.g. RAW16) don't have the conversion stage, they are not
* croppable. The crop region will be ignored by raw streams.</p>
- * <p>For non-raw streams, any additional per-stream cropping will
- * be done to maximize the final pixel area of the stream.</p>
- * <p>For example, if the crop region is set to a 4:3 aspect
- * ratio, then 4:3 streams will use the exact crop
- * region. 16:9 streams will further crop vertically
- * (letterbox).</p>
- * <p>Conversely, if the crop region is set to a 16:9, then 4:3
- * outputs will crop horizontally (pillarbox), and 16:9
- * streams will match exactly. These additional crops will
- * be centered within the crop region.</p>
+ * <p>For non-raw streams, any additional per-stream cropping will be done to maximize the
+ * final pixel area of the stream.</p>
+ * <p>For example, if the crop region is set to a 4:3 aspect ratio, then 4:3 streams will use
+ * the exact crop region. 16:9 streams will further crop vertically (letterbox).</p>
+ * <p>Conversely, if the crop region is set to a 16:9, then 4:3 outputs will crop horizontally
+ * (pillarbox), and 16:9 streams will match exactly. These additional crops will be
+ * centered within the crop region.</p>
+ * <p>To illustrate, here are several scenarios of different crop regions and output streams,
+ * for a hypothetical camera device with an active array of size <code>(2000,1500)</code>. Note that
+ * several of these examples use non-centered crop regions for ease of illustration; such
+ * regions are only supported on devices with FREEFORM capability
+ * (ACAMERA_SCALER_CROPPING_TYPE <code>== FREEFORM</code>), but this does not affect the way the crop
+ * rules work otherwise.</p>
+ * <ul>
+ * <li>Camera Configuration:<ul>
+ * <li>Active array size: <code>2000x1500</code> (3 MP, 4:3 aspect ratio)</li>
+ * <li>Output stream #1: <code>640x480</code> (VGA, 4:3 aspect ratio)</li>
+ * <li>Output stream #2: <code>1280x720</code> (720p, 16:9 aspect ratio)</li>
+ * </ul>
+ * </li>
+ * <li>Case #1: 4:3 crop region with 2x digital zoom<ul>
+ * <li>Crop region: <code>Rect(500, 375, 1500, 1125) // (left, top, right, bottom)</code></li>
+ * <li><img alt="4:3 aspect ratio crop diagram" src="../images/camera2/metadata/android.scaler.cropRegion/crop-region-43-ratio.png" /></li>
+ * <li><code>640x480</code> stream source area: <code>(500, 375, 1500, 1125)</code> (equal to crop region)</li>
+ * <li><code>1280x720</code> stream source area: <code>(500, 469, 1500, 1031)</code> (letterboxed)</li>
+ * </ul>
+ * </li>
+ * <li>Case #2: 16:9 crop region with ~1.5x digital zoom.<ul>
+ * <li>Crop region: <code>Rect(500, 375, 1833, 1125)</code></li>
+ * <li><img alt="16:9 aspect ratio crop diagram" src="../images/camera2/metadata/android.scaler.cropRegion/crop-region-169-ratio.png" /></li>
+ * <li><code>640x480</code> stream source area: <code>(666, 375, 1666, 1125)</code> (pillarboxed)</li>
+ * <li><code>1280x720</code> stream source area: <code>(500, 375, 1833, 1125)</code> (equal to crop region)</li>
+ * </ul>
+ * </li>
+ * <li>Case #3: 1:1 crop region with ~2.6x digital zoom.<ul>
+ * <li>Crop region: <code>Rect(500, 375, 1250, 1125)</code></li>
+ * <li><img alt="1:1 aspect ratio crop diagram" src="../images/camera2/metadata/android.scaler.cropRegion/crop-region-11-ratio.png" /></li>
+ * <li><code>640x480</code> stream source area: <code>(500, 469, 1250, 1031)</code> (letterboxed)</li>
+ * <li><code>1280x720</code> stream source area: <code>(500, 543, 1250, 957)</code> (letterboxed)</li>
+ * </ul>
+ * </li>
+ * <li>Case #4: Replace <code>640x480</code> stream with <code>1024x1024</code> stream, with 4:3 crop region:<ul>
+ * <li>Crop region: <code>Rect(500, 375, 1500, 1125)</code></li>
+ * <li><img alt="Square output, 4:3 aspect ratio crop diagram" src="../images/camera2/metadata/android.scaler.cropRegion/crop-region-43-square-ratio.png" /></li>
+ * <li><code>1024x1024</code> stream source area: <code>(625, 375, 1375, 1125)</code> (pillarboxed)</li>
+ * <li><code>1280x720</code> stream source area: <code>(500, 469, 1500, 1031)</code> (letterboxed)</li>
+ * <li>Note that in this case, neither of the two outputs is a subset of the other, with
+ * each containing image data the other doesn't have.</li>
+ * </ul>
+ * </li>
+ * </ul>
* <p>If the coordinate system is ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE, the width and height
* of the crop region cannot be set to be smaller than
* <code>floor( activeArraySize.width / ACAMERA_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM )</code> and
@@ -3199,14 +3371,22 @@
* and
* <code>floor( preCorrectionActiveArraySize.height / ACAMERA_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM )</code>,
* respectively.</p>
- * <p>The camera device may adjust the crop region to account
- * for rounding and other hardware requirements; the final
- * crop region used will be included in the output capture
- * result.</p>
+ * <p>The camera device may adjust the crop region to account for rounding and other hardware
+ * requirements; the final crop region used will be included in the output capture result.</p>
+ * <p>Starting from API level 30, it's strongly recommended to use ACAMERA_CONTROL_ZOOM_RATIO
+ * to take advantage of better support for zoom with logical multi-camera. The benefits
+ * include better precision with optical-digital zoom combination, and ability to do
+ * zoom-out from 1.0x. When using ACAMERA_CONTROL_ZOOM_RATIO for zoom, the crop region in
+ * the capture request must be either letterboxing or pillarboxing (but not both). The
+ * coordinate system is post-zoom, meaning that the activeArraySize or
+ * preCorrectionActiveArraySize covers the camera device's field of view "after" zoom. See
+ * ACAMERA_CONTROL_ZOOM_RATIO for details.</p>
* <p>The data representation is int[4], which maps to (left, top, width, height).</p>
*
+ * @see ACAMERA_CONTROL_ZOOM_RATIO
* @see ACAMERA_DISTORTION_CORRECTION_MODE
* @see ACAMERA_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
+ * @see ACAMERA_SCALER_CROPPING_TYPE
* @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
* @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
@@ -3232,6 +3412,12 @@
* <p>Crop regions that have a width or height that is smaller
* than this ratio allows will be rounded up to the minimum
* allowed size by the camera device.</p>
+ * <p>Starting from API level 30, when using ACAMERA_CONTROL_ZOOM_RATIO to zoom in or out,
+ * the application must use ACAMERA_CONTROL_ZOOM_RATIO_RANGE to query both the minimum and
+ * maximum zoom ratio.</p>
+ *
+ * @see ACAMERA_CONTROL_ZOOM_RATIO
+ * @see ACAMERA_CONTROL_ZOOM_RATIO_RANGE
*/
ACAMERA_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM = // float
ACAMERA_SCALER_START + 4,
@@ -3406,8 +3592,24 @@
* <p>Camera devices that support FREEFORM cropping will support any crop region that
* is inside of the active array. The camera device will apply the same crop region and
* return the final used crop region in capture result metadata ACAMERA_SCALER_CROP_REGION.</p>
+ * <p>Starting from API level 30,</p>
+ * <ul>
+ * <li>If the camera device supports FREEFORM cropping, in order to do FREEFORM cropping, the
+ * application must set ACAMERA_CONTROL_ZOOM_RATIO to 1.0, and use ACAMERA_SCALER_CROP_REGION
+ * for zoom.</li>
+ * <li>To do CENTER_ONLY zoom, the application has below 2 options:<ol>
+ * <li>Set ACAMERA_CONTROL_ZOOM_RATIO to 1.0; adjust zoom by ACAMERA_SCALER_CROP_REGION.</li>
+ * <li>Adjust zoom by ACAMERA_CONTROL_ZOOM_RATIO; use ACAMERA_SCALER_CROP_REGION to crop
+ * the field of view vertically (letterboxing) or horizontally (pillarboxing), but not
+ * windowboxing.</li>
+ * </ol>
+ * </li>
+ * <li>Setting ACAMERA_CONTROL_ZOOM_RATIO to values different than 1.0 and
+ * ACAMERA_SCALER_CROP_REGION to be windowboxing at the same time is undefined behavior.</li>
+ * </ul>
* <p>LEGACY capability devices will only support CENTER_ONLY cropping.</p>
*
+ * @see ACAMERA_CONTROL_ZOOM_RATIO
* @see ACAMERA_SCALER_CROP_REGION
* @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
*/
@@ -3488,6 +3690,108 @@
ACAMERA_SCALER_AVAILABLE_RECOMMENDED_INPUT_OUTPUT_FORMATS_MAP =
// int32
ACAMERA_SCALER_START + 15,
+ /**
+ * <p>List of rotate-and-crop modes for ACAMERA_SCALER_ROTATE_AND_CROP that are supported by this camera device.</p>
+ *
+ * @see ACAMERA_SCALER_ROTATE_AND_CROP
+ *
+ * <p>Type: byte[n]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>This entry lists the valid modes for ACAMERA_SCALER_ROTATE_AND_CROP for this camera device.</p>
+ * <p>Starting with API level 30, all devices will list at least <code>ROTATE_AND_CROP_NONE</code>.
+ * Devices with support for rotate-and-crop will additionally list at least
+ * <code>ROTATE_AND_CROP_AUTO</code> and <code>ROTATE_AND_CROP_90</code>.</p>
+ *
+ * @see ACAMERA_SCALER_ROTATE_AND_CROP
+ */
+ ACAMERA_SCALER_AVAILABLE_ROTATE_AND_CROP_MODES = // byte[n]
+ ACAMERA_SCALER_START + 16,
+ /**
+ * <p>Whether a rotation-and-crop operation is applied to processed
+ * outputs from the camera.</p>
+ *
+ * <p>Type: byte (acamera_metadata_enum_android_scaler_rotate_and_crop_t)</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
+ * <li>ACaptureRequest</li>
+ * </ul></p>
+ *
+ * <p>This control is primarily intended to help camera applications with no support for
+ * multi-window modes to work correctly on devices where multi-window scenarios are
+ * unavoidable, such as foldables or other devices with variable display geometry or more
+ * free-form window placement (such as laptops, which often place portrait-orientation apps
+ * in landscape with pillarboxing).</p>
+ * <p>If supported, the default value is <code>ROTATE_AND_CROP_AUTO</code>, which allows the camera API
+ * to enable backwards-compatibility support for applications that do not support resizing
+ * / multi-window modes, when the device is in fact in a multi-window mode (such as inset
+ * portrait on laptops, or on a foldable device in some fold states). In addition,
+ * <code>ROTATE_AND_CROP_NONE</code> and <code>ROTATE_AND_CROP_90</code> will always be available if this control
+ * is supported by the device. If not supported, devices API level 30 or higher will always
+ * list only <code>ROTATE_AND_CROP_NONE</code>.</p>
+ * <p>When <code>CROP_AUTO</code> is in use, and the camera API activates backward-compatibility mode,
+ * several metadata fields will also be parsed differently to ensure that coordinates are
+ * correctly handled for features like drawing face detection boxes or passing in
+ * tap-to-focus coordinates. The camera API will convert positions in the active array
+ * coordinate system to/from the cropped-and-rotated coordinate system to make the
+ * operation transparent for applications. The following controls are affected:</p>
+ * <ul>
+ * <li>ACAMERA_CONTROL_AE_REGIONS</li>
+ * <li>ACAMERA_CONTROL_AF_REGIONS</li>
+ * <li>ACAMERA_CONTROL_AWB_REGIONS</li>
+ * <li>android.statistics.faces</li>
+ * </ul>
+ * <p>Capture results will contain the actual value selected by the API;
+ * <code>ROTATE_AND_CROP_AUTO</code> will never be seen in a capture result.</p>
+ * <p>Applications can also select their preferred cropping mode, either to opt out of the
+ * backwards-compatibility treatment, or to use the cropping feature themselves as needed.
+ * In this case, no coordinate translation will be done automatically, and all controls
+ * will continue to use the normal active array coordinates.</p>
+ * <p>Cropping and rotating is done after the application of digital zoom (via either
+ * ACAMERA_SCALER_CROP_REGION or ACAMERA_CONTROL_ZOOM_RATIO), but before each individual
+ * output is further cropped and scaled. It only affects processed outputs such as
+ * YUV, PRIVATE, and JPEG. It has no effect on RAW outputs.</p>
+ * <p>When <code>CROP_90</code> or <code>CROP_270</code> are selected, there is a significant loss to the field of
+ * view. For example, with a 4:3 aspect ratio output of 1600x1200, <code>CROP_90</code> will still
+ * produce 1600x1200 output, but these buffers are cropped from a vertical 3:4 slice at the
+ * center of the 4:3 area, then rotated to be 4:3, and then upscaled to 1600x1200. Only
+ * 56.25% of the original FOV is still visible. In general, for an aspect ratio of <code>w:h</code>,
+ * the crop and rotate operation leaves <code>(h/w)^2</code> of the field of view visible. For 16:9,
+ * this is ~31.6%.</p>
+ * <p>As a visual example, the figure below shows the effect of <code>ROTATE_AND_CROP_90</code> on the
+ * outputs for the following parameters:</p>
+ * <ul>
+ * <li>Sensor active array: <code>2000x1500</code></li>
+ * <li>Crop region: top-left: <code>(500, 375)</code>, size: <code>(1000, 750)</code> (4:3 aspect ratio)</li>
+ * <li>Output streams: YUV <code>640x480</code> and YUV <code>1280x720</code></li>
+ * <li><code>ROTATE_AND_CROP_90</code></li>
+ * </ul>
+ * <p><img alt="Effect of ROTATE_AND_CROP_90" src="../images/camera2/metadata/android.scaler.rotateAndCrop/crop-region-rotate-90-43-ratio.png" /></p>
+ * <p>With these settings, the regions of the active array covered by the output streams are:</p>
+ * <ul>
+ * <li>640x480 stream crop: top-left: <code>(219, 375)</code>, size: <code>(562, 750)</code></li>
+ * <li>1280x720 stream crop: top-left: <code>(289, 375)</code>, size: <code>(422, 750)</code></li>
+ * </ul>
+ * <p>Since the buffers are rotated, the buffers as seen by the application are:</p>
+ * <ul>
+ * <li>640x480 stream: top-left: <code>(781, 375)</code> on active array, size: <code>(640, 480)</code>, downscaled 1.17x from sensor pixels</li>
+ * <li>1280x720 stream: top-left: <code>(711, 375)</code> on active array, size: <code>(1280, 720)</code>, upscaled 1.71x from sensor pixels</li>
+ * </ul>
+ *
+ * @see ACAMERA_CONTROL_AE_REGIONS
+ * @see ACAMERA_CONTROL_AF_REGIONS
+ * @see ACAMERA_CONTROL_AWB_REGIONS
+ * @see ACAMERA_CONTROL_ZOOM_RATIO
+ * @see ACAMERA_SCALER_CROP_REGION
+ */
+ ACAMERA_SCALER_ROTATE_AND_CROP = // byte (acamera_metadata_enum_android_scaler_rotate_and_crop_t)
+ ACAMERA_SCALER_START + 17,
ACAMERA_SCALER_END,
/**
@@ -4707,9 +5011,20 @@
* When the distortion correction mode is not OFF, the coordinate system follows
* ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE, with
* <code>(0, 0)</code> being the top-left pixel of the active array.</p>
- * <p>Only available if ACAMERA_STATISTICS_FACE_DETECT_MODE == FULL</p>
+ * <p>Only available if ACAMERA_STATISTICS_FACE_DETECT_MODE == FULL.</p>
+ * <p>Starting from API level 30, the coordinate system of activeArraySize or
+ * preCorrectionActiveArraySize is used to represent post-zoomRatio field of view, not
+ * pre-zoomRatio field of view. This means that if the relative position of faces and
+ * the camera device doesn't change, when zooming in by increasing
+ * ACAMERA_CONTROL_ZOOM_RATIO, the face landmarks move farther away from the center of the
+ * activeArray or preCorrectionActiveArray. If ACAMERA_CONTROL_ZOOM_RATIO is set to 1.0
+ * (default), the face landmarks coordinates won't change as ACAMERA_SCALER_CROP_REGION
+ * changes. See ACAMERA_CONTROL_ZOOM_RATIO for details. Whether to use activeArraySize or
+ * preCorrectionActiveArraySize still depends on distortion correction mode.</p>
*
+ * @see ACAMERA_CONTROL_ZOOM_RATIO
* @see ACAMERA_DISTORTION_CORRECTION_MODE
+ * @see ACAMERA_SCALER_CROP_REGION
* @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
* @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
* @see ACAMERA_STATISTICS_FACE_DETECT_MODE
@@ -4738,10 +5053,21 @@
* When the distortion correction mode is not OFF, the coordinate system follows
* ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE, with
* <code>(0, 0)</code> being the top-left pixel of the active array.</p>
- * <p>Only available if ACAMERA_STATISTICS_FACE_DETECT_MODE != OFF
- * The data representation is <code>int[4]</code>, which maps to <code>(left, top, right, bottom)</code>.</p>
+ * <p>Only available if ACAMERA_STATISTICS_FACE_DETECT_MODE != OFF.</p>
+ * <p>Starting from API level 30, the coordinate system of activeArraySize or
+ * preCorrectionActiveArraySize is used to represent post-zoomRatio field of view, not
+ * pre-zoomRatio field of view. This means that if the relative position of faces and
+ * the camera device doesn't change, when zooming in by increasing
+ * ACAMERA_CONTROL_ZOOM_RATIO, the face rectangles grow larger and move farther away from
+ * the center of the activeArray or preCorrectionActiveArray. If ACAMERA_CONTROL_ZOOM_RATIO
+ * is set to 1.0 (default), the face rectangles won't change as ACAMERA_SCALER_CROP_REGION
+ * changes. See ACAMERA_CONTROL_ZOOM_RATIO for details. Whether to use activeArraySize or
+ * preCorrectionActiveArraySize still depends on distortion correction mode.</p>
+ * <p>The data representation is <code>int[4]</code>, which maps to <code>(left, top, right, bottom)</code>.</p>
*
+ * @see ACAMERA_CONTROL_ZOOM_RATIO
* @see ACAMERA_DISTORTION_CORRECTION_MODE
+ * @see ACAMERA_SCALER_CROP_REGION
* @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
* @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
* @see ACAMERA_STATISTICS_FACE_DETECT_MODE
@@ -7324,6 +7650,19 @@
*/
ACAMERA_LENS_POSE_REFERENCE_GYROSCOPE = 1,
+ /**
+ * <p>The camera device cannot represent the values of ACAMERA_LENS_POSE_TRANSLATION
+ * and ACAMERA_LENS_POSE_ROTATION accurately enough. One such example is a camera device
+ * on the cover of a foldable phone: in order to measure the pose translation and rotation,
+ * some kind of hinge position sensor would be needed.</p>
+ * <p>The value of ACAMERA_LENS_POSE_TRANSLATION must be all zeros, and
+ * ACAMERA_LENS_POSE_ROTATION must be values matching its default facing.</p>
+ *
+ * @see ACAMERA_LENS_POSE_ROTATION
+ * @see ACAMERA_LENS_POSE_TRANSLATION
+ */
+ ACAMERA_LENS_POSE_REFERENCE_UNDEFINED = 2,
+
} acamera_metadata_enum_android_lens_pose_reference_t;
@@ -7704,14 +8043,20 @@
* <p>The camera device is a logical camera backed by two or more physical cameras.</p>
* <p>In API level 28, the physical cameras must also be exposed to the application via
* <a href="https://developer.android.com/reference/android/hardware/camera2/CameraManager.html#getCameraIdList">CameraManager#getCameraIdList</a>.</p>
- * <p>Starting from API level 29, some or all physical cameras may not be independently
- * exposed to the application, in which case the physical camera IDs will not be
- * available in <a href="https://developer.android.com/reference/android/hardware/camera2/CameraManager.html#getCameraIdList">CameraManager#getCameraIdList</a>. But the
+ * <p>Starting from API level 29:</p>
+ * <ul>
+ * <li>Some or all physical cameras may not be independently exposed to the application,
+ * in which case the physical camera IDs will not be available in
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraManager.html#getCameraIdList">CameraManager#getCameraIdList</a>. But the
* application can still query the physical cameras' characteristics by calling
- * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraManager.html#getCameraCharacteristics">CameraManager#getCameraCharacteristics</a>. Additionally,
- * if a physical camera is hidden from camera ID list, the mandatory stream combinations
- * for that physical camera must be supported through the logical camera using physical
- * streams.</p>
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraManager.html#getCameraCharacteristics">CameraManager#getCameraCharacteristics</a>.</li>
+ * <li>If a physical camera is hidden from camera ID list, the mandatory stream
+ * combinations for that physical camera must be supported through the logical camera
+ * using physical streams. One exception is that in API level 30, a physical camera
+ * may become unavailable via
+ * {@link ACameraManager_PhysicalCameraAvailabilityCallback }
+ * callback.</li>
+ * </ul>
* <p>Combinations of logical and physical streams, or physical streams from different
* physical cameras are not guaranteed. However, if the camera device supports
* {@link ACameraDevice_isSessionConfigurationSupported },
@@ -7969,6 +8314,51 @@
} acamera_metadata_enum_android_scaler_available_recommended_stream_configurations_t;
+// ACAMERA_SCALER_ROTATE_AND_CROP
+typedef enum acamera_metadata_enum_acamera_scaler_rotate_and_crop {
+ /**
+ * <p>No rotate and crop is applied. Processed outputs are in the sensor orientation.</p>
+ */
+ ACAMERA_SCALER_ROTATE_AND_CROP_NONE = 0,
+
+ /**
+ * <p>Processed images are rotated by 90 degrees clockwise, and then cropped
+ * to the original aspect ratio.</p>
+ */
+ ACAMERA_SCALER_ROTATE_AND_CROP_90 = 1,
+
+ /**
+ * <p>Processed images are rotated by 180 degrees. Since the aspect ratio does not
+ * change, no cropping is performed.</p>
+ */
+ ACAMERA_SCALER_ROTATE_AND_CROP_180 = 2,
+
+ /**
+ * <p>Processed images are rotated by 270 degrees clockwise, and then cropped
+ * to the original aspect ratio.</p>
+ */
+ ACAMERA_SCALER_ROTATE_AND_CROP_270 = 3,
+
+ /**
+ * <p>The camera API automatically selects the best concrete value for
+ * rotate-and-crop based on the application's support for resizability and the current
+ * multi-window mode.</p>
+ * <p>If the application does not support resizing but the display mode for its main
+ * Activity is not in a typical orientation, the camera API will set <code>ROTATE_AND_CROP_90</code>
+ * or some other supported rotation value, depending on device configuration,
+ * to ensure preview and captured images are correctly shown to the user. Otherwise,
+ * <code>ROTATE_AND_CROP_NONE</code> will be selected.</p>
+ * <p>When a value other than NONE is selected, several metadata fields will also be parsed
+ * differently to ensure that coordinates are correctly handled for features like drawing
+ * face detection boxes or passing in tap-to-focus coordinates. The camera API will
+ * convert positions in the active array coordinate system to/from the cropped-and-rotated
+ * coordinate system to make the operation transparent for applications.</p>
+ * <p>No coordinate mapping will be done when the application selects a non-AUTO mode.</p>
+ */
+ ACAMERA_SCALER_ROTATE_AND_CROP_AUTO = 4,
+
+} acamera_metadata_enum_android_scaler_rotate_and_crop_t;
+
// ACAMERA_SENSOR_REFERENCE_ILLUMINANT1
typedef enum acamera_metadata_enum_acamera_sensor_reference_illuminant1 {
@@ -8165,12 +8555,16 @@
// ACAMERA_SENSOR_INFO_TIMESTAMP_SOURCE
typedef enum acamera_metadata_enum_acamera_sensor_info_timestamp_source {
/**
- * <p>Timestamps from ACAMERA_SENSOR_TIMESTAMP are in nanoseconds and monotonic,
- * but can not be compared to timestamps from other subsystems
- * (e.g. accelerometer, gyro etc.), or other instances of the same or different
- * camera devices in the same system. Timestamps between streams and results for
- * a single camera instance are comparable, and the timestamps for all buffers
- * and the result metadata generated by a single capture are identical.</p>
+ * <p>Timestamps from ACAMERA_SENSOR_TIMESTAMP are in nanoseconds and monotonic, but can
+ * not be compared to timestamps from other subsystems (e.g. accelerometer, gyro etc.),
+ * or other instances of the same or different camera devices in the same system with
+ * accuracy. However, the timestamps are roughly in the same timebase as
+ * <a href="https://developer.android.com/reference/android/os/SystemClock.html#uptimeMillis">SystemClock#uptimeMillis</a>. The accuracy is sufficient for tasks
+ * like A/V synchronization for video recording, at least, and the timestamps can be
+ * directly used together with timestamps from the audio subsystem for that task.</p>
+ * <p>Timestamps between streams and results for a single camera instance are comparable,
+ * and the timestamps for all buffers and the result metadata generated by a single
+ * capture are identical.</p>
*
* @see ACAMERA_SENSOR_TIMESTAMP
*/
@@ -8180,6 +8574,14 @@
* <p>Timestamps from ACAMERA_SENSOR_TIMESTAMP are in the same timebase as
* <a href="https://developer.android.com/reference/android/os/SystemClock.html#elapsedRealtimeNanos">SystemClock#elapsedRealtimeNanos</a>,
* and they can be compared to other timestamps using that base.</p>
+ * <p>When buffers from a REALTIME device are passed directly to a video encoder from the
+ * camera, automatic compensation is done to account for differing timebases of the
+ * audio and camera subsystems. If the application is receiving buffers and then later
+ * sending them to a video encoder or other application where they are compared with
+ * audio subsystem timestamps or similar, this compensation is not present. In those
+ * cases, applications need to adjust the timestamps themselves. Since <a href="https://developer.android.com/reference/android/os/SystemClock.html#elapsedRealtimeNanos">SystemClock#elapsedRealtimeNanos</a> and <a href="https://developer.android.com/reference/android/os/SystemClock.html#uptimeMillis">SystemClock#uptimeMillis</a> only diverge while the device is asleep, an
+ * offset between the two sources can be measured once per active session and applied
+ * to timestamps for sufficient accuracy for A/V sync.</p>
*
* @see ACAMERA_SENSOR_TIMESTAMP
*/
diff --git a/camera/ndk/libcamera2ndk.map.txt b/camera/ndk/libcamera2ndk.map.txt
index b6f1553..2b630db 100644
--- a/camera/ndk/libcamera2ndk.map.txt
+++ b/camera/ndk/libcamera2ndk.map.txt
@@ -31,6 +31,7 @@
ACameraMetadata_getAllTags;
ACameraMetadata_getConstEntry;
ACameraMetadata_isLogicalMultiCamera; # introduced=29
+ ACameraMetadata_fromCameraMetadata; # introduced=30
ACameraOutputTarget_create;
ACameraOutputTarget_free;
ACaptureRequest_addTarget;
diff --git a/camera/tests/CameraBinderTests.cpp b/camera/tests/CameraBinderTests.cpp
index 9a18b10..571cf59 100644
--- a/camera/tests/CameraBinderTests.cpp
+++ b/camera/tests/CameraBinderTests.cpp
@@ -83,6 +83,12 @@
return binder::Status::ok();
};
+ virtual binder::Status onPhysicalCameraStatusChanged(int32_t /*status*/,
+ const String16& /*cameraId*/, const String16& /*physicalCameraId*/) {
+ // No op
+ return binder::Status::ok();
+ };
+
virtual binder::Status onTorchStatusChanged(int32_t status, const String16& cameraId) {
Mutex::Autolock l(mLock);
mCameraTorchStatuses[cameraId] = status;
@@ -498,7 +504,9 @@
EXPECT_TRUE(res.isOk()) << res;
EXPECT_LE(0, streamId);
CameraMetadata sessionParams;
- res = device->endConfigure(/*isConstrainedHighSpeed*/ false, sessionParams);
+ std::vector<int> offlineStreamIds;
+ res = device->endConfigure(/*isConstrainedHighSpeed*/ false, sessionParams,
+ &offlineStreamIds);
EXPECT_TRUE(res.isOk()) << res;
EXPECT_FALSE(callbacks->hadError());
@@ -609,7 +617,8 @@
EXPECT_TRUE(res.isOk()) << res;
res = device->deleteStream(streamId);
EXPECT_TRUE(res.isOk()) << res;
- res = device->endConfigure(/*isConstrainedHighSpeed*/ false, sessionParams);
+ res = device->endConfigure(/*isConstrainedHighSpeed*/ false, sessionParams,
+ &offlineStreamIds);
EXPECT_TRUE(res.isOk()) << res;
sleep(/*second*/1); // allow some time for errors to show up, if any
diff --git a/cmds/screenrecord/Android.bp b/cmds/screenrecord/Android.bp
index 72008c2..d7d905f 100644
--- a/cmds/screenrecord/Android.bp
+++ b/cmds/screenrecord/Android.bp
@@ -26,6 +26,7 @@
header_libs: [
"libmediadrm_headers",
+ "libmediametrics_headers",
],
shared_libs: [
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index defc94f..6470fb1 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -8,6 +8,9 @@
jpeg.cpp \
SineSource.cpp
+LOCAL_HEADER_LIBRARIES := \
+ libmediametrics_headers \
+
LOCAL_SHARED_LIBRARIES := \
libstagefright libmedia libmedia_codeclist libutils libbinder \
libstagefright_foundation libjpeg libui libgui libcutils liblog \
@@ -23,6 +26,7 @@
LOCAL_MODULE_TAGS := optional
+LOCAL_SYSTEM_EXT_MODULE:= true
LOCAL_MODULE:= stagefright
include $(BUILD_EXECUTABLE)
@@ -36,6 +40,9 @@
SineSource.cpp \
record.cpp
+LOCAL_HEADER_LIBRARIES := \
+ libmediametrics_headers \
+
LOCAL_SHARED_LIBRARIES := \
libstagefright libmedia liblog libutils libbinder \
libstagefright_foundation libdatasource libaudioclient
@@ -62,6 +69,9 @@
AudioPlayer.cpp \
recordvideo.cpp
+LOCAL_HEADER_LIBRARIES := \
+ libmediametrics_headers \
+
LOCAL_SHARED_LIBRARIES := \
libstagefright libmedia liblog libutils libbinder \
libstagefright_foundation libaudioclient
@@ -89,6 +99,9 @@
SineSource.cpp \
audioloop.cpp
+LOCAL_HEADER_LIBRARIES := \
+ libmediametrics_headers \
+
LOCAL_SHARED_LIBRARIES := \
libstagefright libmedia liblog libutils libbinder \
libstagefright_foundation libaudioclient
@@ -112,6 +125,9 @@
LOCAL_SRC_FILES:= \
stream.cpp \
+LOCAL_HEADER_LIBRARIES := \
+ libmediametrics_headers \
+
LOCAL_SHARED_LIBRARIES := \
libstagefright liblog libutils libbinder libui libgui \
libstagefright_foundation libmedia libcutils libdatasource
@@ -138,6 +154,7 @@
LOCAL_HEADER_LIBRARIES := \
libmediadrm_headers \
+ libmediametrics_headers \
LOCAL_SHARED_LIBRARIES := \
libstagefright liblog libutils libbinder libstagefright_foundation \
@@ -167,6 +184,7 @@
LOCAL_HEADER_LIBRARIES := \
libmediadrm_headers \
+ libmediametrics_headers \
LOCAL_SHARED_LIBRARIES := \
libstagefright \
@@ -208,6 +226,9 @@
LOCAL_SRC_FILES:= \
muxer.cpp \
+LOCAL_HEADER_LIBRARIES := \
+ libmediametrics_headers \
+
LOCAL_SHARED_LIBRARIES := \
libstagefright liblog libutils libbinder libstagefright_foundation \
libcutils libc
diff --git a/cmds/stagefright/AudioPlayer.cpp b/cmds/stagefright/AudioPlayer.cpp
index 208713d..eb76953 100644
--- a/cmds/stagefright/AudioPlayer.cpp
+++ b/cmds/stagefright/AudioPlayer.cpp
@@ -23,7 +23,7 @@
#include <binder/IPCThreadState.h>
#include <media/AudioTrack.h>
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/openmax/OMX_Audio.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/ALookup.h>
diff --git a/cmds/stagefright/AudioPlayer.h b/cmds/stagefright/AudioPlayer.h
index 7c2c36f..107bd71 100644
--- a/cmds/stagefright/AudioPlayer.h
+++ b/cmds/stagefright/AudioPlayer.h
@@ -18,7 +18,7 @@
#define AUDIO_PLAYER_H_
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/MediaPlayerInterface.h>
#include <media/stagefright/MediaBuffer.h>
#include <utils/threads.h>
diff --git a/cmds/stagefright/SineSource.h b/cmds/stagefright/SineSource.h
index 1817291..6f1d98c 100644
--- a/cmds/stagefright/SineSource.h
+++ b/cmds/stagefright/SineSource.h
@@ -2,7 +2,7 @@
#define SINE_SOURCE_H_
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <utils/Compat.h>
namespace android {
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index e8cfece..c430f05 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -33,7 +33,7 @@
#include <binder/ProcessState.h>
#include <datasource/DataSourceFactory.h>
#include <media/DataSource.h>
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/IMediaHTTPService.h>
#include <media/IMediaPlayerService.h>
#include <media/stagefright/foundation/ABuffer.h>
diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp
index 5f03929..250d26b 100644
--- a/cmds/stagefright/stream.cpp
+++ b/cmds/stagefright/stream.cpp
@@ -26,7 +26,7 @@
#include <media/IMediaHTTPService.h>
#include <media/IStreamSource.h>
#include <media/mediaplayer.h>
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/InterfaceUtils.h>
diff --git a/drm/drmserver/DrmManager.cpp b/drm/drmserver/DrmManager.cpp
index e2ea83a..38dc052 100644
--- a/drm/drmserver/DrmManager.cpp
+++ b/drm/drmserver/DrmManager.cpp
@@ -31,7 +31,7 @@
#include <drm/DrmInfoRequest.h>
#include <drm/DrmSupportInfo.h>
#include <drm/DrmConvertedStatus.h>
-#include <media/MediaAnalyticsItem.h>
+#include <media/MediaMetricsItem.h>
#include <IDrmEngine.h>
#include "DrmManager.h"
@@ -58,7 +58,7 @@
const char func[], const String8& plugInId, const String8& mimeType) {
IDrmEngine& engine = mPlugInManager.getPlugIn(plugInId);
- std::unique_ptr<MediaAnalyticsItem> item(MediaAnalyticsItem::create("drmmanager"));
+ std::unique_ptr<mediametrics::Item> item(mediametrics::Item::create("drmmanager"));
item->setUid(IPCThreadState::self()->getCallingUid());
item->setCString("function_name", func);
item->setCString("plugin_id", plugInId.getPathLeaf().getBasePath().c_str());
diff --git a/drm/libmediadrm/Android.bp b/drm/libmediadrm/Android.bp
index 6b1b475..6ba1b9c 100644
--- a/drm/libmediadrm/Android.bp
+++ b/drm/libmediadrm/Android.bp
@@ -165,4 +165,9 @@
"libmediadrmmetrics_full",
"libutils",
],
+
+ header_libs: [
+ "libmediametrics_headers",
+ "libstagefright_foundation_headers",
+ ],
}
diff --git a/drm/libmediadrm/tests/Android.bp b/drm/libmediadrm/tests/Android.bp
index 7471e05..6529387 100644
--- a/drm/libmediadrm/tests/Android.bp
+++ b/drm/libmediadrm/tests/Android.bp
@@ -3,7 +3,10 @@
cc_test {
name: "CounterMetric_test",
srcs: ["CounterMetric_test.cpp"],
- header_libs: ["libmedia_headers"],
+ header_libs: [
+ "libmedia_headers",
+ "libmediametrics_headers",
+ ],
shared_libs: ["libmediadrm"],
cflags: [
"-Werror",
@@ -46,7 +49,8 @@
name: "EventMetric_test",
srcs: ["EventMetric_test.cpp"],
header_libs: [
- "libmedia_headers"
+ "libmedia_headers",
+ "libmediametrics_headers",
],
shared_libs: [
"liblog",
diff --git a/drm/mediadrm/plugins/clearkey/hidl/Android.bp b/drm/mediadrm/plugins/clearkey/hidl/Android.bp
index eb623f9..a194416 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/Android.bp
+++ b/drm/mediadrm/plugins/clearkey/hidl/Android.bp
@@ -78,18 +78,37 @@
},
srcs: ["protos/DeviceFiles.proto"],
}
+
cc_binary {
name: "android.hardware.drm@1.2-service.clearkey",
defaults: ["clearkey_service_defaults"],
srcs: ["service.cpp"],
init_rc: ["android.hardware.drm@1.2-service.clearkey.rc"],
- vintf_fragments: ["manifest_android.hardware.drm@1.3-service.clearkey.xml"],
+ vintf_fragments: ["manifest_android.hardware.drm@1.2-service.clearkey.xml"],
}
+
cc_binary {
name: "android.hardware.drm@1.2-service-lazy.clearkey",
overrides: ["android.hardware.drm@1.2-service.clearkey"],
defaults: ["clearkey_service_defaults"],
srcs: ["serviceLazy.cpp"],
init_rc: ["android.hardware.drm@1.2-service-lazy.clearkey.rc"],
+ vintf_fragments: ["manifest_android.hardware.drm@1.2-service.clearkey.xml"],
+}
+
+cc_binary {
+ name: "android.hardware.drm@1.3-service.clearkey",
+ defaults: ["clearkey_service_defaults"],
+ srcs: ["service.cpp"],
+ init_rc: ["android.hardware.drm@1.3-service.clearkey.rc"],
+ vintf_fragments: ["manifest_android.hardware.drm@1.3-service.clearkey.xml"],
+}
+
+cc_binary {
+ name: "android.hardware.drm@1.3-service-lazy.clearkey",
+ overrides: ["android.hardware.drm@1.3-service.clearkey"],
+ defaults: ["clearkey_service_defaults"],
+ srcs: ["serviceLazy.cpp"],
+ init_rc: ["android.hardware.drm@1.3-service-lazy.clearkey.rc"],
vintf_fragments: ["manifest_android.hardware.drm@1.3-service.clearkey.xml"],
}
diff --git a/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp
index f164f28..3ecf6d5 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp
@@ -136,6 +136,8 @@
return Void();
}
+ base = static_cast<uint8_t *>(static_cast<void *>(destBase->getPointer()));
+
if (destBuffer.offset + destBuffer.size > destBase->getSize()) {
_hidl_cb(Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE, 0, "invalid buffer size");
return Void();
diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
index 942ea7d..546eb3e 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
@@ -829,6 +829,12 @@
// and the drm service. The clearkey implementation consists of:
// count - number of secure stops
// list of fixed length secure stops
+ size_t countBufferSize = sizeof(uint32_t);
+ if (input.size() < countBufferSize) {
+ // SafetyNet logging
+ android_errorWriteLog(0x534e4554, "144766455");
+ return Status::BAD_VALUE;
+ }
uint32_t count = 0;
sscanf(reinterpret_cast<char*>(input.data()), "%04" PRIu32, &count);
diff --git a/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.2-service-lazy.clearkey.rc b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.2-service-lazy.clearkey.rc
index cff4d74..9afd3d7 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.2-service-lazy.clearkey.rc
+++ b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.2-service-lazy.clearkey.rc
@@ -5,8 +5,6 @@
interface android.hardware.drm@1.1::IDrmFactory clearkey
interface android.hardware.drm@1.2::ICryptoFactory clearkey
interface android.hardware.drm@1.2::IDrmFactory clearkey
- interface android.hardware.drm@1.3::ICryptoFactory clearkey
- interface android.hardware.drm@1.3::IDrmFactory clearkey
disabled
oneshot
class hal
diff --git a/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.2-service.clearkey.rc b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.2-service.clearkey.rc
index dca232a..c1abe7f 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.2-service.clearkey.rc
+++ b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.2-service.clearkey.rc
@@ -5,8 +5,7 @@
interface android.hardware.drm@1.1::IDrmFactory clearkey
interface android.hardware.drm@1.2::ICryptoFactory clearkey
interface android.hardware.drm@1.2::IDrmFactory clearkey
- interface android.hardware.drm@1.3::ICryptoFactory clearkey
- interface android.hardware.drm@1.3::IDrmFactory clearkey
+ disabled
class hal
user media
group media mediadrm
diff --git a/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.3-service-lazy.clearkey.rc b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.3-service-lazy.clearkey.rc
new file mode 100644
index 0000000..1e0d431
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.3-service-lazy.clearkey.rc
@@ -0,0 +1,16 @@
+service vendor.drm-clearkey-hal-1-3 /vendor/bin/hw/android.hardware.drm@1.3-service-lazy.clearkey
+ interface android.hardware.drm@1.0::ICryptoFactory clearkey
+ interface android.hardware.drm@1.0::IDrmFactory clearkey
+ interface android.hardware.drm@1.1::ICryptoFactory clearkey
+ interface android.hardware.drm@1.1::IDrmFactory clearkey
+ interface android.hardware.drm@1.2::ICryptoFactory clearkey
+ interface android.hardware.drm@1.2::IDrmFactory clearkey
+ interface android.hardware.drm@1.3::ICryptoFactory clearkey
+ interface android.hardware.drm@1.3::IDrmFactory clearkey
+ disabled
+ oneshot
+ class hal
+ user media
+ group media mediadrm
+ ioprio rt 4
+ writepid /dev/cpuset/foreground/tasks
diff --git a/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.3-service.clearkey.rc b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.3-service.clearkey.rc
new file mode 100644
index 0000000..8130511
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.3-service.clearkey.rc
@@ -0,0 +1,14 @@
+service vendor.drm-clearkey-hal-1-3 /vendor/bin/hw/android.hardware.drm@1.3-service.clearkey
+ interface android.hardware.drm@1.0::ICryptoFactory clearkey
+ interface android.hardware.drm@1.0::IDrmFactory clearkey
+ interface android.hardware.drm@1.1::ICryptoFactory clearkey
+ interface android.hardware.drm@1.1::IDrmFactory clearkey
+ interface android.hardware.drm@1.2::ICryptoFactory clearkey
+ interface android.hardware.drm@1.2::IDrmFactory clearkey
+ interface android.hardware.drm@1.3::ICryptoFactory clearkey
+ interface android.hardware.drm@1.3::IDrmFactory clearkey
+ class hal
+ user media
+ group media mediadrm
+ ioprio rt 4
+ writepid /dev/cpuset/foreground/tasks
diff --git a/drm/mediadrm/plugins/clearkey/hidl/manifest_android.hardware.drm@1.2-service.clearkey.xml b/drm/mediadrm/plugins/clearkey/hidl/manifest_android.hardware.drm@1.2-service.clearkey.xml
new file mode 100644
index 0000000..16cba11
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/hidl/manifest_android.hardware.drm@1.2-service.clearkey.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.drm</name>
+ <transport>hwbinder</transport>
+ <fqname>@1.2::ICryptoFactory/clearkey</fqname>
+ <fqname>@1.2::IDrmFactory/clearkey</fqname>
+ </hal>
+</manifest>
diff --git a/include/media/DataSource.h b/include/media/DataSource.h
index 8efa809..2abd5f5 100644
--- a/include/media/DataSource.h
+++ b/include/media/DataSource.h
@@ -22,7 +22,7 @@
#include <android/IDataSource.h>
#include <media/stagefright/MediaErrors.h>
-#include <media/DataSourceBase.h>
+#include <media/stagefright/DataSourceBase.h>
#include <media/MediaExtractorPluginApi.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
diff --git a/include/media/DataSourceBase.h b/include/media/DataSourceBase.h
deleted file mode 120000
index d2ab2f1..0000000
--- a/include/media/DataSourceBase.h
+++ /dev/null
@@ -1 +0,0 @@
-stagefright/DataSourceBase.h
\ No newline at end of file
diff --git a/include/media/IMediaAnalyticsService.h b/include/media/IMediaAnalyticsService.h
deleted file mode 120000
index a596d60..0000000
--- a/include/media/IMediaAnalyticsService.h
+++ /dev/null
@@ -1 +0,0 @@
-../../media/libmediametrics/include/IMediaAnalyticsService.h
\ No newline at end of file
diff --git a/include/media/MediaAnalyticsItem.h b/include/media/MediaAnalyticsItem.h
deleted file mode 120000
index e8124e0..0000000
--- a/include/media/MediaAnalyticsItem.h
+++ /dev/null
@@ -1 +0,0 @@
-../../media/libmediametrics/include/MediaAnalyticsItem.h
\ No newline at end of file
diff --git a/include/media/MediaMetrics.h b/include/media/MediaMetrics.h
deleted file mode 120000
index 5f757e4..0000000
--- a/include/media/MediaMetrics.h
+++ /dev/null
@@ -1 +0,0 @@
-../../media/libmediametrics/include/MediaMetrics.h
\ No newline at end of file
diff --git a/include/media/MediaSource.h b/include/media/MediaSource.h
deleted file mode 120000
index 34bf65d..0000000
--- a/include/media/MediaSource.h
+++ /dev/null
@@ -1 +0,0 @@
-../../media/libstagefright/include/media/stagefright/MediaSource.h
\ No newline at end of file
diff --git a/include/media/audiohal b/include/media/audiohal
deleted file mode 120000
index f400582..0000000
--- a/include/media/audiohal
+++ /dev/null
@@ -1 +0,0 @@
-../../media/libaudiohal/include/media/audiohal/
\ No newline at end of file
diff --git a/include/media/nbaio/AudioStreamOutSink.h b/include/media/nbaio/AudioStreamOutSink.h
deleted file mode 120000
index 43bfac5..0000000
--- a/include/media/nbaio/AudioStreamOutSink.h
+++ /dev/null
@@ -1 +0,0 @@
-../../../media/libnbaio/include/media/nbaio/AudioStreamOutSink.h
\ No newline at end of file
diff --git a/include/media/nbaio/NBAIO.h b/include/media/nbaio/NBAIO.h
deleted file mode 120000
index ff6a151..0000000
--- a/include/media/nbaio/NBAIO.h
+++ /dev/null
@@ -1 +0,0 @@
-../../../media/libnbaio/include_mono/media/nbaio/NBAIO.h
\ No newline at end of file
diff --git a/include/media/nbaio/SingleStateQueue.h b/include/media/nbaio/SingleStateQueue.h
deleted file mode 120000
index d3e0553..0000000
--- a/include/media/nbaio/SingleStateQueue.h
+++ /dev/null
@@ -1 +0,0 @@
-../../../media/libnbaio/include_mono/media/nbaio/SingleStateQueue.h
\ No newline at end of file
diff --git a/include/media/stagefright b/include/media/stagefright
deleted file mode 120000
index 5393f68..0000000
--- a/include/media/stagefright
+++ /dev/null
@@ -1 +0,0 @@
-../../media/libstagefright/include/media/stagefright/
\ No newline at end of file
diff --git a/include/soundtrigger/ISoundTrigger.h b/include/soundtrigger/ISoundTrigger.h
deleted file mode 100644
index d628c05..0000000
--- a/include/soundtrigger/ISoundTrigger.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_ISOUNDTRIGGER_H
-#define ANDROID_HARDWARE_ISOUNDTRIGGER_H
-
-#include <utils/RefBase.h>
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-#include <binder/IMemory.h>
-#include <system/sound_trigger.h>
-
-namespace android {
-
-class ISoundTrigger : public IInterface
-{
-public:
- DECLARE_META_INTERFACE(SoundTrigger);
-
- virtual void detach() = 0;
-
- virtual status_t loadSoundModel(const sp<IMemory>& modelMemory,
- sound_model_handle_t *handle) = 0;
-
- virtual status_t unloadSoundModel(sound_model_handle_t handle) = 0;
-
- virtual status_t startRecognition(sound_model_handle_t handle,
- const sp<IMemory>& dataMemory) = 0;
- virtual status_t stopRecognition(sound_model_handle_t handle) = 0;
- virtual status_t getModelState(sound_model_handle_t handle) = 0;
- virtual status_t setParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t param, int32_t value) = 0;
- virtual status_t getParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t param, int32_t* value) = 0;
- virtual status_t queryParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t param,
- sound_trigger_model_parameter_range_t* param_range) = 0;
-};
-
-// ----------------------------------------------------------------------------
-
-class BnSoundTrigger: public BnInterface<ISoundTrigger>
-{
-public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-}; // namespace android
-
-#endif //ANDROID_HARDWARE_ISOUNDTRIGGER_H
diff --git a/include/soundtrigger/ISoundTriggerClient.h b/include/soundtrigger/ISoundTriggerClient.h
deleted file mode 100644
index 480429a..0000000
--- a/include/soundtrigger/ISoundTriggerClient.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_ISOUNDTRIGGER_CLIENT_H
-#define ANDROID_HARDWARE_ISOUNDTRIGGER_CLIENT_H
-
-#include <utils/RefBase.h>
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-
-namespace android {
-
-class ISoundTriggerClient : public IInterface
-{
-public:
-
- DECLARE_META_INTERFACE(SoundTriggerClient);
-
- virtual void onRecognitionEvent(const sp<IMemory>& eventMemory) = 0;
-
- virtual void onSoundModelEvent(const sp<IMemory>& eventMemory) = 0;
-
- virtual void onServiceStateChange(const sp<IMemory>& eventMemory) = 0;
-
-};
-
-// ----------------------------------------------------------------------------
-
-class BnSoundTriggerClient : public BnInterface<ISoundTriggerClient>
-{
-public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-}; // namespace android
-
-#endif //ANDROID_HARDWARE_ISOUNDTRIGGER_CLIENT_H
diff --git a/include/soundtrigger/ISoundTriggerHwService.h b/include/soundtrigger/ISoundTriggerHwService.h
deleted file mode 100644
index 1faeb0f..0000000
--- a/include/soundtrigger/ISoundTriggerHwService.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_ISOUNDTRIGGER_SERVICE_H
-#define ANDROID_HARDWARE_ISOUNDTRIGGER_SERVICE_H
-
-#include <utils/RefBase.h>
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-#include <system/sound_trigger.h>
-
-namespace android {
-
-class ISoundTrigger;
-class ISoundTriggerClient;
-
-class ISoundTriggerHwService : public IInterface
-{
-public:
-
- DECLARE_META_INTERFACE(SoundTriggerHwService);
-
- virtual status_t listModules(const String16& opPackageName,
- struct sound_trigger_module_descriptor *modules,
- uint32_t *numModules) = 0;
-
- virtual status_t attach(const String16& opPackageName,
- const sound_trigger_module_handle_t handle,
- const sp<ISoundTriggerClient>& client,
- sp<ISoundTrigger>& module) = 0;
-
- virtual status_t setCaptureState(bool active) = 0;
-};
-
-// ----------------------------------------------------------------------------
-
-class BnSoundTriggerHwService: public BnInterface<ISoundTriggerHwService>
-{
-public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-}; // namespace android
-
-#endif //ANDROID_HARDWARE_ISOUNDTRIGGER_SERVICE_H
diff --git a/include/soundtrigger/OWNERS b/include/soundtrigger/OWNERS
deleted file mode 100644
index e83f6b9..0000000
--- a/include/soundtrigger/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-elaurent@google.com
-thorntonc@google.com
diff --git a/include/soundtrigger/SoundTrigger.h b/include/soundtrigger/SoundTrigger.h
deleted file mode 100644
index 248620a..0000000
--- a/include/soundtrigger/SoundTrigger.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_SOUNDTRIGGER_H
-#define ANDROID_HARDWARE_SOUNDTRIGGER_H
-
-#include <binder/IBinder.h>
-#include <utils/threads.h>
-#include <soundtrigger/SoundTriggerCallback.h>
-#include <soundtrigger/ISoundTrigger.h>
-#include <soundtrigger/ISoundTriggerHwService.h>
-#include <soundtrigger/ISoundTriggerClient.h>
-#include <system/sound_trigger.h>
-
-namespace android {
-
-class MemoryDealer;
-
-class SoundTrigger : public BnSoundTriggerClient,
- public IBinder::DeathRecipient
-{
-public:
-
- virtual ~SoundTrigger();
-
- static status_t listModules(const String16& opPackageName,
- struct sound_trigger_module_descriptor *modules,
- uint32_t *numModules);
- static sp<SoundTrigger> attach(const String16& opPackageName,
- const sound_trigger_module_handle_t module,
- const sp<SoundTriggerCallback>& callback);
-
- static status_t setCaptureState(bool active);
-
- void detach();
-
- status_t loadSoundModel(const sp<IMemory>& modelMemory,
- sound_model_handle_t *handle);
-
- status_t unloadSoundModel(sound_model_handle_t handle);
-
- status_t startRecognition(sound_model_handle_t handle, const sp<IMemory>& dataMemory);
- status_t stopRecognition(sound_model_handle_t handle);
- status_t getModelState(sound_model_handle_t handle);
- status_t setParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t param, int32_t value);
- status_t getParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t param, int32_t* value);
- status_t queryParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t param,
- sound_trigger_model_parameter_range_t* param_range);
-
- // BpSoundTriggerClient
- virtual void onRecognitionEvent(const sp<IMemory>& eventMemory);
- virtual void onSoundModelEvent(const sp<IMemory>& eventMemory);
- virtual void onServiceStateChange(const sp<IMemory>& eventMemory);
-
- //IBinder::DeathRecipient
- virtual void binderDied(const wp<IBinder>& who);
-
- static status_t stringToGuid(const char *str, sound_trigger_uuid_t *guid);
- static status_t guidToString(const sound_trigger_uuid_t *guid,
- char *str, size_t maxLen);
-
-private:
- SoundTrigger(sound_trigger_module_handle_t module,
- const sp<SoundTriggerCallback>&);
- static const sp<ISoundTriggerHwService> getSoundTriggerHwService();
-
- Mutex mLock;
- sp<ISoundTrigger> mISoundTrigger;
- sp<SoundTriggerCallback> mCallback;
-};
-
-}; // namespace android
-
-#endif //ANDROID_HARDWARE_SOUNDTRIGGER_H
diff --git a/include/soundtrigger/SoundTriggerCallback.h b/include/soundtrigger/SoundTriggerCallback.h
deleted file mode 100644
index b5277f2..0000000
--- a/include/soundtrigger/SoundTriggerCallback.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_SOUNDTRIGGER_CALLBACK_H
-#define ANDROID_HARDWARE_SOUNDTRIGGER_CALLBACK_H
-
-#include <utils/RefBase.h>
-#include <system/sound_trigger.h>
-
-namespace android {
-
-class SoundTriggerCallback : public RefBase
-{
-public:
-
- SoundTriggerCallback() {}
- virtual ~SoundTriggerCallback() {}
-
- virtual void onRecognitionEvent(struct sound_trigger_recognition_event *event) = 0;
-
- virtual void onSoundModelEvent(struct sound_trigger_model_event *event) = 0;
-
- virtual void onServiceStateChange(sound_trigger_service_state_t state) = 0;
-
- virtual void onServiceDied() = 0;
-
-};
-
-}; // namespace android
-
-#endif //ANDROID_HARDWARE_SOUNDTRIGGER_CALLBACK_H
diff --git a/media/audioserver/Android.mk b/media/audioserver/Android.mk
index e33804d..f44f0d5 100644
--- a/media/audioserver/Android.mk
+++ b/media/audioserver/Android.mk
@@ -19,10 +19,13 @@
libmediautils \
libnbaio \
libnblog \
- libsoundtriggerservice \
libutils \
libvibrator
+LOCAL_HEADER_LIBRARIES := \
+ libaudiohal_headers \
+ libmediametrics_headers \
+
# TODO oboeservice is the old folder name for aaudioservice. It will be changed.
LOCAL_C_INCLUDES := \
frameworks/av/services/audioflinger \
@@ -33,7 +36,6 @@
frameworks/av/services/audiopolicy/service \
frameworks/av/services/medialog \
frameworks/av/services/oboeservice \
- frameworks/av/services/soundtrigger \
frameworks/av/media/libaaudio/include \
frameworks/av/media/libaaudio/src \
frameworks/av/media/libaaudio/src/binding \
diff --git a/media/audioserver/main_audioserver.cpp b/media/audioserver/main_audioserver.cpp
index db57248..4a2ac1d 100644
--- a/media/audioserver/main_audioserver.cpp
+++ b/media/audioserver/main_audioserver.cpp
@@ -36,7 +36,6 @@
#include "utility/AAudioUtilities.h"
#include "MediaLogService.h"
#include "MediaUtils.h"
-#include "SoundTriggerHwService.h"
using namespace android;
@@ -148,7 +147,6 @@
AAudioService::instantiate();
}
- SoundTriggerHwService::instantiate();
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
diff --git a/media/bufferpool/2.0/Android.bp b/media/bufferpool/2.0/Android.bp
index 97f114a..557b7ef 100644
--- a/media/bufferpool/2.0/Android.bp
+++ b/media/bufferpool/2.0/Android.bp
@@ -30,6 +30,8 @@
name: "libstagefright_bufferpool@2.0.1",
defaults: ["libstagefright_bufferpool@2.0-default"],
vendor_available: true,
+ // TODO: b/147147992
+ double_loadable: true,
cflags: [
"-DBUFFERPOOL_CLONE_HANDLES",
],
@@ -40,6 +42,8 @@
name: "libstagefright_bufferpool@2.0",
defaults: ["libstagefright_bufferpool@2.0-default"],
vendor_available: true,
+ // TODO: b/147147992
+ double_loadable: true,
vndk: {
enabled: true,
},
diff --git a/media/codec2/TEST_MAPPING b/media/codec2/TEST_MAPPING
new file mode 100644
index 0000000..e01f452
--- /dev/null
+++ b/media/codec2/TEST_MAPPING
@@ -0,0 +1,31 @@
+{
+ "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"
+ }
+ ]
+ }
+ ],
+ "imports": [
+ {
+ "path": "frameworks/av/drm/mediadrm/plugins"
+ }
+ ]
+}
diff --git a/media/codec2/components/avc/C2SoftAvcDec.cpp b/media/codec2/components/avc/C2SoftAvcDec.cpp
index 2662f0f..56813c4 100644
--- a/media/codec2/components/avc/C2SoftAvcDec.cpp
+++ b/media/codec2/components/avc/C2SoftAvcDec.cpp
@@ -501,7 +501,7 @@
status_t C2SoftAvcDec::initDecoder() {
if (OK != createDecoder()) return UNKNOWN_ERROR;
mNumCores = MIN(getCpuCoreCount(), MAX_NUM_CORES);
- mStride = ALIGN64(mWidth);
+ mStride = ALIGN128(mWidth);
mSignalledError = false;
resetPlugin();
(void) setNumCores();
@@ -756,8 +756,8 @@
ALOGE("not supposed to be here, invalid decoder context");
return C2_CORRUPTED;
}
- if (mStride != ALIGN64(mWidth)) {
- mStride = ALIGN64(mWidth);
+ if (mStride != ALIGN128(mWidth)) {
+ mStride = ALIGN128(mWidth);
if (OK != setParams(mStride, IVD_DECODE_FRAME)) return C2_CORRUPTED;
}
if (mOutBlock &&
@@ -909,7 +909,7 @@
if (0 < s_decode_op.u4_pic_wd && 0 < s_decode_op.u4_pic_ht) {
if (mHeaderDecoded == false) {
mHeaderDecoded = true;
- setParams(ALIGN64(s_decode_op.u4_pic_wd), IVD_DECODE_FRAME);
+ setParams(ALIGN128(s_decode_op.u4_pic_wd), IVD_DECODE_FRAME);
}
if (s_decode_op.u4_pic_wd != mWidth || s_decode_op.u4_pic_ht != mHeight) {
mWidth = s_decode_op.u4_pic_wd;
diff --git a/media/codec2/components/avc/C2SoftAvcDec.h b/media/codec2/components/avc/C2SoftAvcDec.h
index 4414a26..ed27493 100644
--- a/media/codec2/components/avc/C2SoftAvcDec.h
+++ b/media/codec2/components/avc/C2SoftAvcDec.h
@@ -40,6 +40,7 @@
#define ivdext_ctl_get_vui_params_ip_t ih264d_ctl_get_vui_params_ip_t
#define ivdext_ctl_get_vui_params_op_t ih264d_ctl_get_vui_params_op_t
#define ALIGN64(x) ((((x) + 63) >> 6) << 6)
+#define ALIGN128(x) ((((x) + 127) >> 7) << 7)
#define MAX_NUM_CORES 4
#define IVDEXT_CMD_CTL_SET_NUM_CORES \
(IVD_CONTROL_API_COMMAND_TYPE_T)IH264D_CMD_CTL_SET_NUM_CORES
diff --git a/media/codec2/components/cmds/codec2.cpp b/media/codec2/components/cmds/codec2.cpp
index 38eaf88..d6025de 100644
--- a/media/codec2/components/cmds/codec2.cpp
+++ b/media/codec2/components/cmds/codec2.cpp
@@ -34,7 +34,7 @@
#include <media/DataSource.h>
#include <mediadrm/ICrypto.h>
#include <media/IMediaHTTPService.h>
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ALooper.h>
#include <media/stagefright/foundation/AMessage.h>
diff --git a/media/codec2/components/hevc/C2SoftHevcDec.cpp b/media/codec2/components/hevc/C2SoftHevcDec.cpp
index df677c2..6db4387 100644
--- a/media/codec2/components/hevc/C2SoftHevcDec.cpp
+++ b/media/codec2/components/hevc/C2SoftHevcDec.cpp
@@ -497,7 +497,7 @@
status_t C2SoftHevcDec::initDecoder() {
if (OK != createDecoder()) return UNKNOWN_ERROR;
mNumCores = MIN(getCpuCoreCount(), MAX_NUM_CORES);
- mStride = ALIGN64(mWidth);
+ mStride = ALIGN128(mWidth);
mSignalledError = false;
resetPlugin();
(void) setNumCores();
@@ -752,8 +752,8 @@
ALOGE("not supposed to be here, invalid decoder context");
return C2_CORRUPTED;
}
- if (mStride != ALIGN64(mWidth)) {
- mStride = ALIGN64(mWidth);
+ if (mStride != ALIGN128(mWidth)) {
+ mStride = ALIGN128(mWidth);
if (OK != setParams(mStride, IVD_DECODE_FRAME)) return C2_CORRUPTED;
}
if (mOutBlock &&
@@ -904,7 +904,7 @@
if (0 < s_decode_op.u4_pic_wd && 0 < s_decode_op.u4_pic_ht) {
if (mHeaderDecoded == false) {
mHeaderDecoded = true;
- setParams(ALIGN64(s_decode_op.u4_pic_wd), IVD_DECODE_FRAME);
+ setParams(ALIGN128(s_decode_op.u4_pic_wd), IVD_DECODE_FRAME);
}
if (s_decode_op.u4_pic_wd != mWidth || s_decode_op.u4_pic_ht != mHeight) {
mWidth = s_decode_op.u4_pic_wd;
diff --git a/media/codec2/components/hevc/C2SoftHevcDec.h b/media/codec2/components/hevc/C2SoftHevcDec.h
index ce63a6c..aecd101 100644
--- a/media/codec2/components/hevc/C2SoftHevcDec.h
+++ b/media/codec2/components/hevc/C2SoftHevcDec.h
@@ -38,6 +38,7 @@
#define ivdext_ctl_get_vui_params_ip_t ihevcd_cxa_ctl_get_vui_params_ip_t
#define ivdext_ctl_get_vui_params_op_t ihevcd_cxa_ctl_get_vui_params_op_t
#define ALIGN64(x) ((((x) + 63) >> 6) << 6)
+#define ALIGN128(x) ((((x) + 127) >> 7) << 7)
#define MAX_NUM_CORES 4
#define IVDEXT_CMD_CTL_SET_NUM_CORES \
(IVD_CONTROL_API_COMMAND_TYPE_T)IHEVCD_CXA_CMD_CTL_SET_NUM_CORES
diff --git a/media/codec2/components/raw/C2SoftRawDec.cpp b/media/codec2/components/raw/C2SoftRawDec.cpp
index 95eb909..7b6f21a 100644
--- a/media/codec2/components/raw/C2SoftRawDec.cpp
+++ b/media/codec2/components/raw/C2SoftRawDec.cpp
@@ -58,7 +58,7 @@
addParameter(
DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE)
.withDefault(new C2StreamSampleRateInfo::output(0u, 44100))
- .withFields({C2F(mSampleRate, value).inRange(8000, 384000)})
+ .withFields({C2F(mSampleRate, value).greaterThan(0)})
.withSetter((Setter<decltype(*mSampleRate)>::StrictValueWithNoDeps))
.build());
diff --git a/media/codec2/core/Android.bp b/media/codec2/core/Android.bp
index a7e8997..1f9d7ab 100644
--- a/media/codec2/core/Android.bp
+++ b/media/codec2/core/Android.bp
@@ -10,6 +10,7 @@
vndk: {
enabled: true,
},
+ double_loadable: true,
srcs: ["C2.cpp"],
diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h
index a9928b3..f111f81 100644
--- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h
+++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h
@@ -206,10 +206,23 @@
std::mutex mMutex;
sp<ClientManager> mSenderManager;
sp<IClientManager> mReceiverManager;
- int64_t mReceiverConnectionId;
- int64_t mSourceConnectionId;
- std::chrono::steady_clock::time_point mLastSent;
std::chrono::steady_clock::duration mRefreshInterval;
+
+ struct Connection {
+ int64_t receiverConnectionId;
+ std::chrono::steady_clock::time_point lastSent;
+ Connection(int64_t receiverConnectionId,
+ std::chrono::steady_clock::time_point lastSent)
+ : receiverConnectionId(receiverConnectionId),
+ lastSent(lastSent) {
+ }
+ };
+
+ // Map of connections.
+ //
+ // The key is the connection id. One sender-receiver pair may have multiple
+ // connections.
+ std::map<int64_t, Connection> mConnections;
};
// std::list<std::unique_ptr<C2Work>> -> WorkBundle
diff --git a/media/codec2/hidl/1.0/utils/types.cpp b/media/codec2/hidl/1.0/utils/types.cpp
index 04fa59c..c73cb52 100644
--- a/media/codec2/hidl/1.0/utils/types.cpp
+++ b/media/codec2/hidl/1.0/utils/types.cpp
@@ -969,8 +969,6 @@
const sp<IClientManager>& receiverManager,
std::chrono::steady_clock::duration refreshInterval)
: mReceiverManager(receiverManager),
- mSourceConnectionId(0),
- mLastSent(std::chrono::steady_clock::now()),
mRefreshInterval(refreshInterval) {
}
@@ -980,6 +978,7 @@
std::lock_guard<std::mutex> lock(mMutex);
if (mReceiverManager != receiverManager) {
mReceiverManager = receiverManager;
+ mConnections.clear();
}
mRefreshInterval = refreshInterval;
}
@@ -987,12 +986,16 @@
ResultStatus DefaultBufferPoolSender::send(
const std::shared_ptr<BufferPoolData>& bpData,
BufferStatusMessage* bpMessage) {
+ int64_t connectionId = bpData->mConnectionId;
+ if (connectionId == 0) {
+ LOG(WARNING) << "registerSender -- invalid sender connection id (0).";
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ std::lock_guard<std::mutex> lock(mMutex);
if (!mReceiverManager) {
LOG(ERROR) << "No access to receiver's BufferPool.";
return ResultStatus::NOT_FOUND;
}
- ResultStatus rs;
- std::lock_guard<std::mutex> lock(mMutex);
if (!mSenderManager) {
mSenderManager = ClientManager::getInstance();
if (!mSenderManager) {
@@ -1000,52 +1003,61 @@
return ResultStatus::CRITICAL_ERROR;
}
}
- int64_t connectionId = bpData->mConnectionId;
+
+ int64_t receiverConnectionId{0};
+ auto foundConnection = mConnections.find(connectionId);
+ bool isNewConnection = foundConnection == mConnections.end();
std::chrono::steady_clock::time_point now =
std::chrono::steady_clock::now();
- std::chrono::steady_clock::duration interval = now - mLastSent;
- if (mSourceConnectionId == 0 ||
- mSourceConnectionId != connectionId ||
- interval > mRefreshInterval) {
+ if (isNewConnection ||
+ (now - foundConnection->second.lastSent > mRefreshInterval)) {
// Initialize the bufferpool connection.
- mSourceConnectionId = connectionId;
- if (mSourceConnectionId == 0) {
- return ResultStatus::CRITICAL_ERROR;
- }
-
- int64_t receiverConnectionId;
- rs = mSenderManager->registerSender(mReceiverManager,
- connectionId,
- &receiverConnectionId);
+ ResultStatus rs =
+ mSenderManager->registerSender(mReceiverManager,
+ connectionId,
+ &receiverConnectionId);
if ((rs != ResultStatus::OK) && (rs != ResultStatus::ALREADY_EXISTS)) {
LOG(WARNING) << "registerSender -- returned error: "
<< static_cast<int32_t>(rs)
<< ".";
return rs;
+ } else if (receiverConnectionId == 0) {
+ LOG(WARNING) << "registerSender -- "
+ "invalid receiver connection id (0).";
+ return ResultStatus::CRITICAL_ERROR;
} else {
- mReceiverConnectionId = receiverConnectionId;
+ if (isNewConnection) {
+ foundConnection = mConnections.try_emplace(
+ connectionId, receiverConnectionId, now).first;
+ } else {
+ foundConnection->second.receiverConnectionId = receiverConnectionId;
+ }
}
+ } else {
+ receiverConnectionId = foundConnection->second.receiverConnectionId;
}
uint64_t transactionId;
int64_t timestampUs;
- rs = mSenderManager->postSend(
- mReceiverConnectionId, bpData, &transactionId, ×tampUs);
+ ResultStatus rs = mSenderManager->postSend(
+ receiverConnectionId, bpData, &transactionId, ×tampUs);
if (rs != ResultStatus::OK) {
LOG(ERROR) << "ClientManager::postSend -- returned error: "
<< static_cast<int32_t>(rs)
<< ".";
+ mConnections.erase(foundConnection);
return rs;
}
if (!bpMessage) {
LOG(ERROR) << "Null output parameter for BufferStatusMessage.";
+ mConnections.erase(foundConnection);
return ResultStatus::CRITICAL_ERROR;
}
- bpMessage->connectionId = mReceiverConnectionId;
+ bpMessage->connectionId = receiverConnectionId;
bpMessage->bufferId = bpData->mId;
bpMessage->transactionId = transactionId;
bpMessage->timestampUs = timestampUs;
- mLastSent = now;
+ foundConnection->second.lastSent = now;
return rs;
}
diff --git a/media/codec2/sfplugin/Android.bp b/media/codec2/sfplugin/Android.bp
index 59ac94b..94034b5 100644
--- a/media/codec2/sfplugin/Android.bp
+++ b/media/codec2/sfplugin/Android.bp
@@ -1,6 +1,8 @@
cc_library_shared {
name: "libsfplugin_ccodec",
+ export_include_dirs: ["include"],
+
srcs: [
"C2OMXNode.cpp",
"CCodec.cpp",
@@ -11,7 +13,6 @@
"Codec2InfoBuilder.cpp",
"PipelineWatcher.cpp",
"ReflectedParamUpdater.cpp",
- "SkipCutBuffer.cpp",
],
cflags: [
@@ -22,6 +23,7 @@
header_libs: [
"libcodec2_internal",
"libmediadrm_headers",
+ "libmediametrics_headers",
"media_ndk_headers",
],
@@ -32,6 +34,7 @@
"android.hardware.media.omx@1.0",
"libbase",
"libbinder",
+ "libcodec2",
"libcodec2_client",
"libcodec2_vndk",
"libcutils",
@@ -51,6 +54,11 @@
"libutils",
],
+ export_shared_lib_headers: [
+ "libcodec2",
+ "libcodec2_client",
+ ],
+
sanitize: {
cfi: true,
misc_undefined: [
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index 31e5406..39263f9 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -40,13 +40,15 @@
#include <media/openmax/OMX_IndexExt.h>
#include <media/stagefright/omx/1.0/WGraphicBufferSource.h>
#include <media/stagefright/omx/OmxGraphicBufferSource.h>
+#include <media/stagefright/CCodec.h>
#include <media/stagefright/BufferProducerWrapper.h>
#include <media/stagefright/MediaCodecConstants.h>
#include <media/stagefright/PersistentSurface.h>
#include "C2OMXNode.h"
-#include "CCodec.h"
#include "CCodecBufferChannel.h"
+#include "CCodecConfig.h"
+#include "Codec2Mapper.h"
#include "InputSurfaceWrapper.h"
extern "C" android::PersistentSurface *CreateInputSurface();
@@ -59,6 +61,7 @@
using ::android::hardware::media::c2::V1_0::IInputSurface;
typedef hardware::media::omx::V1_0::IGraphicBufferSource HGraphicBufferSource;
+typedef CCodecConfig Config;
namespace {
@@ -571,7 +574,8 @@
// CCodec
CCodec::CCodec()
- : mChannel(new CCodecBufferChannel(std::make_shared<CCodecCallbackImpl>(this))) {
+ : mChannel(new CCodecBufferChannel(std::make_shared<CCodecCallbackImpl>(this))),
+ mConfig(new CCodecConfig) {
}
CCodec::~CCodec() {
@@ -662,7 +666,8 @@
}
// initialize config here in case setParameters is called prior to configure
- Mutexed<Config>::Locked config(mConfig);
+ Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
+ const std::unique_ptr<Config> &config = *configLocked;
status_t err = config->initialize(mClient, comp);
if (err != OK) {
ALOGW("Failed to initialize configuration support");
@@ -713,6 +718,11 @@
encoder = false;
}
+ int32_t flags;
+ if (!msg->findInt32("flags", &flags)) {
+ return BAD_VALUE;
+ }
+
// TODO: read from intf()
if ((!encoder) != (comp->getName().find("encoder") == std::string::npos)) {
return UNKNOWN_ERROR;
@@ -736,8 +746,12 @@
setSurface(surface);
}
- Mutexed<Config>::Locked config(mConfig);
+ Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
+ const std::unique_ptr<Config> &config = *configLocked;
config->mUsingSurface = surface != nullptr;
+ config->mBuffersBoundToCodec = ((flags & CONFIGURE_FLAG_USE_BLOCK_MODEL) == 0);
+ ALOGD("[%s] buffers are %sbound to CCodec for this session",
+ comp->getName().c_str(), config->mBuffersBoundToCodec ? "" : "not ");
// Enforce required parameters
int32_t i32;
@@ -1052,7 +1066,8 @@
return;
}
- Mutexed<Config>::Locked config(mConfig);
+ Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
+ const std::unique_ptr<Config> &config = *configLocked;
mCallback->onComponentConfigured(config->mInputFormat, config->mOutputFormat);
}
@@ -1126,7 +1141,8 @@
sp<AMessage> outputFormat;
uint64_t usage = 0;
{
- Mutexed<Config>::Locked config(mConfig);
+ Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
+ const std::unique_ptr<Config> &config = *configLocked;
inputFormat = config->mInputFormat;
outputFormat = config->mOutputFormat;
usage = config->mISConfig ? config->mISConfig->mUsage : 0;
@@ -1170,7 +1186,8 @@
}
status_t CCodec::setupInputSurface(const std::shared_ptr<InputSurfaceWrapper> &surface) {
- Mutexed<Config>::Locked config(mConfig);
+ Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
+ const std::unique_ptr<Config> &config = *configLocked;
config->mUsingSurface = true;
// we are now using surface - apply default color aspects to input format - as well as
@@ -1215,7 +1232,8 @@
sp<AMessage> outputFormat;
uint64_t usage = 0;
{
- Mutexed<Config>::Locked config(mConfig);
+ Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
+ const std::unique_ptr<Config> &config = *configLocked;
inputFormat = config->mInputFormat;
outputFormat = config->mOutputFormat;
usage = config->mISConfig ? config->mISConfig->mUsage : 0;
@@ -1290,19 +1308,22 @@
sp<AMessage> inputFormat;
sp<AMessage> outputFormat;
status_t err2 = OK;
+ bool buffersBoundToCodec = false;
{
- Mutexed<Config>::Locked config(mConfig);
+ Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
+ const std::unique_ptr<Config> &config = *configLocked;
inputFormat = config->mInputFormat;
outputFormat = config->mOutputFormat;
if (config->mInputSurface) {
err2 = config->mInputSurface->start();
}
+ buffersBoundToCodec = config->mBuffersBoundToCodec;
}
if (err2 != OK) {
mCallback->onError(err2, ACTION_CODE_FATAL);
return;
}
- err2 = mChannel->start(inputFormat, outputFormat);
+ err2 = mChannel->start(inputFormat, outputFormat, buffersBoundToCodec);
if (err2 != OK) {
mCallback->onError(err2, ACTION_CODE_FATAL);
return;
@@ -1377,7 +1398,8 @@
}
{
- Mutexed<Config>::Locked config(mConfig);
+ Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
+ const std::unique_ptr<Config> &config = *configLocked;
if (config->mInputSurface) {
config->mInputSurface->disconnect();
config->mInputSurface = nullptr;
@@ -1425,7 +1447,8 @@
}
if (clearInputSurfaceIfNeeded) {
- Mutexed<Config>::Locked config(mConfig);
+ Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
+ const std::unique_ptr<Config> &config = *configLocked;
if (config->mInputSurface) {
config->mInputSurface->disconnect();
config->mInputSurface = nullptr;
@@ -1544,7 +1567,11 @@
return;
}
- (void)mChannel->start(nullptr, nullptr);
+ (void)mChannel->start(nullptr, nullptr, [&]{
+ Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
+ const std::unique_ptr<Config> &config = *configLocked;
+ return config->mBuffersBoundToCodec;
+ }());
{
Mutexed<State>::Locked state(mState);
@@ -1583,7 +1610,8 @@
params->removeEntryAt(params->findEntryByName(KEY_BIT_RATE));
}
- Mutexed<Config>::Locked config(mConfig);
+ Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
+ const std::unique_ptr<Config> &config = *configLocked;
/**
* Handle input surface parameters
@@ -1642,7 +1670,8 @@
comp = state->comp;
}
ALOGV("request IDR");
- Mutexed<Config>::Locked config(mConfig);
+ Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
+ const std::unique_ptr<Config> &config = *configLocked;
std::vector<std::unique_ptr<C2Param>> params;
params.push_back(
std::make_unique<C2StreamRequestSyncFrameTuning::output>(0u, true));
@@ -1661,7 +1690,8 @@
mChannel->onInputBufferDone(frameIndex, arrayIndex);
if (arrayIndex == 0) {
// We always put no more than one buffer per work, if we use an input surface.
- Mutexed<Config>::Locked config(mConfig);
+ Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
+ const std::unique_ptr<Config> &config = *configLocked;
if (config->mInputSurface) {
config->mInputSurface->onInputBufferDone(frameIndex);
}
@@ -1738,7 +1768,8 @@
}
// handle configuration changes in work done
- Mutexed<Config>::Locked config(mConfig);
+ Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
+ const std::unique_ptr<Config> &config = *configLocked;
bool changed = false;
Config::Watcher<C2StreamInitDataInfo::output> initData =
config->watch<C2StreamInitDataInfo::output>();
@@ -1867,14 +1898,8 @@
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
}
-} // namespace android
-
-extern "C" android::CodecBase *CreateCodec() {
- return new android::CCodec;
-}
-
-// Create Codec 2.0 input surface
-extern "C" android::PersistentSurface *CreateInputSurface() {
+// static
+PersistentSurface *CCodec::CreateInputSurface() {
using namespace android;
using ::android::hardware::media::omx::V1_0::implementation::TWGraphicBufferSource;
// Attempt to create a Codec2's input surface.
@@ -1901,3 +1926,243 @@
inputSurface->getHalInterface()));
}
+static status_t GetCommonAllocatorIds(
+ const std::vector<std::string> &names,
+ C2Allocator::type_t type,
+ std::set<C2Allocator::id_t> *ids) {
+ int poolMask = GetCodec2PoolMask();
+ C2PlatformAllocatorStore::id_t preferredLinearId = GetPreferredLinearAllocatorId(poolMask);
+ C2Allocator::id_t defaultAllocatorId =
+ (type == C2Allocator::LINEAR) ? preferredLinearId : C2PlatformAllocatorStore::GRALLOC;
+
+ ids->clear();
+ if (names.empty()) {
+ return OK;
+ }
+ std::shared_ptr<Codec2Client::Interface> intf{
+ Codec2Client::CreateInterfaceByName(names[0].c_str())};
+ std::vector<std::unique_ptr<C2Param>> params;
+ c2_status_t err = intf->query(
+ {}, {C2PortAllocatorsTuning::input::PARAM_TYPE}, C2_MAY_BLOCK, ¶ms);
+ if (err == C2_OK && params.size() == 1u) {
+ C2PortAllocatorsTuning::input *allocators =
+ C2PortAllocatorsTuning::input::From(params[0].get());
+ if (allocators && allocators->flexCount() > 0) {
+ ids->insert(allocators->m.values, allocators->m.values + allocators->flexCount());
+ }
+ }
+ if (ids->empty()) {
+ // The component does not advertise allocators. Use default.
+ ids->insert(defaultAllocatorId);
+ }
+ for (size_t i = 1; i < names.size(); ++i) {
+ intf = Codec2Client::CreateInterfaceByName(names[i].c_str());
+ err = intf->query(
+ {}, {C2PortAllocatorsTuning::input::PARAM_TYPE}, C2_MAY_BLOCK, ¶ms);
+ bool filtered = false;
+ if (err == C2_OK && params.size() == 1u) {
+ C2PortAllocatorsTuning::input *allocators =
+ C2PortAllocatorsTuning::input::From(params[0].get());
+ if (allocators && allocators->flexCount() > 0) {
+ filtered = true;
+ for (auto it = ids->begin(); it != ids->end(); ) {
+ bool found = false;
+ for (size_t j = 0; j < allocators->flexCount(); ++j) {
+ if (allocators->m.values[j] == *it) {
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ ++it;
+ } else {
+ it = ids->erase(it);
+ }
+ }
+ }
+ }
+ if (!filtered) {
+ // The component does not advertise supported allocators. Use default.
+ bool containsDefault = (ids->count(defaultAllocatorId) > 0u);
+ if (ids->size() != (containsDefault ? 1 : 0)) {
+ ids->clear();
+ if (containsDefault) {
+ ids->insert(defaultAllocatorId);
+ }
+ }
+ }
+ }
+ // Finally, filter with pool masks
+ for (auto it = ids->begin(); it != ids->end(); ) {
+ if ((poolMask >> *it) & 1) {
+ ++it;
+ } else {
+ it = ids->erase(it);
+ }
+ }
+ return OK;
+}
+
+static status_t CalculateMinMaxUsage(
+ const std::vector<std::string> &names, uint64_t *minUsage, uint64_t *maxUsage) {
+ static C2StreamUsageTuning::input sUsage{0u /* stream id */};
+ *minUsage = 0;
+ *maxUsage = ~0ull;
+ for (const std::string &name : names) {
+ std::shared_ptr<Codec2Client::Interface> intf{
+ Codec2Client::CreateInterfaceByName(name.c_str())};
+ std::vector<C2FieldSupportedValuesQuery> fields;
+ fields.push_back(C2FieldSupportedValuesQuery::Possible(
+ C2ParamField{&sUsage, &sUsage.value}));
+ c2_status_t err = intf->querySupportedValues(fields, C2_MAY_BLOCK);
+ if (err != C2_OK) {
+ continue;
+ }
+ if (fields[0].status != C2_OK) {
+ continue;
+ }
+ const C2FieldSupportedValues &supported = fields[0].values;
+ if (supported.type != C2FieldSupportedValues::FLAGS) {
+ continue;
+ }
+ if (supported.values.empty()) {
+ *maxUsage = 0;
+ continue;
+ }
+ *minUsage |= supported.values[0].u64;
+ int64_t currentMaxUsage = 0;
+ for (const C2Value::Primitive &flags : supported.values) {
+ currentMaxUsage |= flags.u64;
+ }
+ *maxUsage &= currentMaxUsage;
+ }
+ return OK;
+}
+
+// static
+status_t CCodec::CanFetchLinearBlock(
+ const std::vector<std::string> &names, const C2MemoryUsage &usage, bool *isCompatible) {
+ uint64_t minUsage = usage.expected;
+ uint64_t maxUsage = ~0ull;
+ std::set<C2Allocator::id_t> allocators;
+ GetCommonAllocatorIds(names, C2Allocator::LINEAR, &allocators);
+ if (allocators.empty()) {
+ *isCompatible = false;
+ return OK;
+ }
+ CalculateMinMaxUsage(names, &minUsage, &maxUsage);
+ *isCompatible = ((maxUsage & minUsage) == minUsage);
+ return OK;
+}
+
+static std::shared_ptr<C2BlockPool> GetPool(C2Allocator::id_t allocId) {
+ static std::mutex sMutex{};
+ static std::map<C2Allocator::id_t, std::shared_ptr<C2BlockPool>> sPools;
+ std::unique_lock<std::mutex> lock{sMutex};
+ std::shared_ptr<C2BlockPool> pool;
+ auto it = sPools.find(allocId);
+ if (it == sPools.end()) {
+ c2_status_t err = CreateCodec2BlockPool(allocId, nullptr, &pool);
+ if (err == OK) {
+ sPools.emplace(allocId, pool);
+ } else {
+ pool.reset();
+ }
+ } else {
+ pool = it->second;
+ }
+ return pool;
+}
+
+// static
+std::shared_ptr<C2LinearBlock> CCodec::FetchLinearBlock(
+ size_t capacity, const C2MemoryUsage &usage, const std::vector<std::string> &names) {
+ uint64_t minUsage = usage.expected;
+ uint64_t maxUsage = ~0ull;
+ std::set<C2Allocator::id_t> allocators;
+ GetCommonAllocatorIds(names, C2Allocator::LINEAR, &allocators);
+ if (allocators.empty()) {
+ allocators.insert(C2PlatformAllocatorStore::DEFAULT_LINEAR);
+ }
+ CalculateMinMaxUsage(names, &minUsage, &maxUsage);
+ if ((maxUsage & minUsage) != minUsage) {
+ allocators.clear();
+ allocators.insert(C2PlatformAllocatorStore::DEFAULT_LINEAR);
+ }
+ std::shared_ptr<C2LinearBlock> block;
+ for (C2Allocator::id_t allocId : allocators) {
+ std::shared_ptr<C2BlockPool> pool = GetPool(allocId);
+ if (!pool) {
+ continue;
+ }
+ c2_status_t err = pool->fetchLinearBlock(capacity, C2MemoryUsage{minUsage}, &block);
+ if (err != C2_OK || !block) {
+ block.reset();
+ continue;
+ }
+ break;
+ }
+ return block;
+}
+
+// static
+status_t CCodec::CanFetchGraphicBlock(
+ const std::vector<std::string> &names, bool *isCompatible) {
+ uint64_t minUsage = 0;
+ uint64_t maxUsage = ~0ull;
+ std::set<C2Allocator::id_t> allocators;
+ GetCommonAllocatorIds(names, C2Allocator::GRAPHIC, &allocators);
+ if (allocators.empty()) {
+ *isCompatible = false;
+ return OK;
+ }
+ CalculateMinMaxUsage(names, &minUsage, &maxUsage);
+ *isCompatible = ((maxUsage & minUsage) == minUsage);
+ return OK;
+}
+
+// static
+std::shared_ptr<C2GraphicBlock> CCodec::FetchGraphicBlock(
+ int32_t width,
+ int32_t height,
+ int32_t format,
+ uint64_t usage,
+ const std::vector<std::string> &names) {
+ uint32_t halPixelFormat = HAL_PIXEL_FORMAT_YCBCR_420_888;
+ if (!C2Mapper::mapPixelFormatFrameworkToCodec(format, &halPixelFormat)) {
+ ALOGD("Unrecognized pixel format: %d", format);
+ return nullptr;
+ }
+ uint64_t minUsage = 0;
+ uint64_t maxUsage = ~0ull;
+ std::set<C2Allocator::id_t> allocators;
+ GetCommonAllocatorIds(names, C2Allocator::GRAPHIC, &allocators);
+ if (allocators.empty()) {
+ allocators.insert(C2PlatformAllocatorStore::DEFAULT_GRAPHIC);
+ }
+ CalculateMinMaxUsage(names, &minUsage, &maxUsage);
+ minUsage |= usage;
+ if ((maxUsage & minUsage) != minUsage) {
+ allocators.clear();
+ allocators.insert(C2PlatformAllocatorStore::DEFAULT_GRAPHIC);
+ }
+ std::shared_ptr<C2GraphicBlock> block;
+ for (C2Allocator::id_t allocId : allocators) {
+ std::shared_ptr<C2BlockPool> pool;
+ c2_status_t err = CreateCodec2BlockPool(allocId, nullptr, &pool);
+ if (err != C2_OK || !pool) {
+ continue;
+ }
+ err = pool->fetchGraphicBlock(
+ width, height, halPixelFormat, C2MemoryUsage{minUsage}, &block);
+ if (err != C2_OK || !block) {
+ block.reset();
+ continue;
+ }
+ break;
+ }
+ return block;
+}
+
+} // namespace android
+
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 2bcb7e2..ae95336 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -29,6 +29,7 @@
#include <android/hardware/cas/native/1.0/IDescrambler.h>
#include <android/hardware/drm/1.0/types.h>
#include <android-base/stringprintf.h>
+#include <binder/MemoryBase.h>
#include <binder/MemoryDealer.h>
#include <cutils/properties.h>
#include <gui/Surface.h>
@@ -41,12 +42,12 @@
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/MediaCodecConstants.h>
+#include <media/stagefright/SkipCutBuffer.h>
#include <media/MediaCodecBuffer.h>
#include <system/window.h>
#include "CCodecBufferChannel.h"
#include "Codec2Buffer.h"
-#include "SkipCutBuffer.h"
namespace android {
@@ -249,7 +250,7 @@
}
CCodecBufferChannel::~CCodecBufferChannel() {
- if (mCrypto != nullptr && mDealer != nullptr && mHeapSeqNum >= 0) {
+ if (mCrypto != nullptr && mHeapSeqNum >= 0) {
mCrypto->unsetHeap(mHeapSeqNum);
}
}
@@ -409,6 +410,173 @@
return OK;
}
+status_t CCodecBufferChannel::attachBuffer(
+ const std::shared_ptr<C2Buffer> &c2Buffer,
+ const sp<MediaCodecBuffer> &buffer) {
+ if (!buffer->copy(c2Buffer)) {
+ return -ENOSYS;
+ }
+ return OK;
+}
+
+void CCodecBufferChannel::ensureDecryptDestination(size_t size) {
+ if (!mDecryptDestination || mDecryptDestination->size() < size) {
+ sp<IMemoryHeap> heap{new MemoryHeapBase(size * 2)};
+ if (mDecryptDestination && mCrypto && mHeapSeqNum >= 0) {
+ mCrypto->unsetHeap(mHeapSeqNum);
+ }
+ mDecryptDestination = new MemoryBase(heap, 0, size * 2);
+ if (mCrypto) {
+ mHeapSeqNum = mCrypto->setHeap(hardware::fromHeap(heap));
+ }
+ }
+}
+
+int32_t CCodecBufferChannel::getHeapSeqNum(const sp<HidlMemory> &memory) {
+ CHECK(mCrypto);
+ auto it = mHeapSeqNumMap.find(memory);
+ int32_t heapSeqNum = -1;
+ if (it == mHeapSeqNumMap.end()) {
+ heapSeqNum = mCrypto->setHeap(memory);
+ mHeapSeqNumMap.emplace(memory, heapSeqNum);
+ } else {
+ heapSeqNum = it->second;
+ }
+ return heapSeqNum;
+}
+
+status_t CCodecBufferChannel::attachEncryptedBuffer(
+ const sp<hardware::HidlMemory> &memory,
+ bool secure,
+ const uint8_t *key,
+ const uint8_t *iv,
+ CryptoPlugin::Mode mode,
+ CryptoPlugin::Pattern pattern,
+ size_t offset,
+ const CryptoPlugin::SubSample *subSamples,
+ size_t numSubSamples,
+ const sp<MediaCodecBuffer> &buffer) {
+ static const C2MemoryUsage kSecureUsage{C2MemoryUsage::READ_PROTECTED, 0};
+ static const C2MemoryUsage kDefaultReadWriteUsage{
+ C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
+
+ size_t size = 0;
+ for (size_t i = 0; i < numSubSamples; ++i) {
+ size += subSamples[i].mNumBytesOfClearData + subSamples[i].mNumBytesOfEncryptedData;
+ }
+ std::shared_ptr<C2BlockPool> pool = mBlockPools.lock()->inputPool;
+ std::shared_ptr<C2LinearBlock> block;
+ c2_status_t err = pool->fetchLinearBlock(
+ size,
+ secure ? kSecureUsage : kDefaultReadWriteUsage,
+ &block);
+ if (err != C2_OK) {
+ return NO_MEMORY;
+ }
+ if (!secure) {
+ ensureDecryptDestination(size);
+ }
+ ssize_t result = -1;
+ ssize_t codecDataOffset = 0;
+ if (mCrypto) {
+ AString errorDetailMsg;
+ int32_t heapSeqNum = getHeapSeqNum(memory);
+ hardware::drm::V1_0::SharedBuffer src{(uint32_t)heapSeqNum, offset, size};
+ hardware::drm::V1_0::DestinationBuffer dst;
+ if (secure) {
+ dst.type = DrmBufferType::NATIVE_HANDLE;
+ dst.secureMemory = hardware::hidl_handle(block->handle());
+ } else {
+ dst.type = DrmBufferType::SHARED_MEMORY;
+ IMemoryToSharedBuffer(
+ mDecryptDestination, mHeapSeqNum, &dst.nonsecureMemory);
+ }
+ result = mCrypto->decrypt(
+ key, iv, mode, pattern, src, 0, subSamples, numSubSamples,
+ dst, &errorDetailMsg);
+ if (result < 0) {
+ return result;
+ }
+ if (dst.type == DrmBufferType::SHARED_MEMORY) {
+ C2WriteView view = block->map().get();
+ if (view.error() != C2_OK) {
+ return false;
+ }
+ if (view.size() < result) {
+ return false;
+ }
+ memcpy(view.data(), mDecryptDestination->unsecurePointer(), result);
+ }
+ } else {
+ // Here we cast CryptoPlugin::SubSample to hardware::cas::native::V1_0::SubSample
+ // directly, the structure definitions should match as checked in DescramblerImpl.cpp.
+ hidl_vec<SubSample> hidlSubSamples;
+ hidlSubSamples.setToExternal((SubSample *)subSamples, numSubSamples, false /*own*/);
+
+ hardware::cas::native::V1_0::SharedBuffer src{*memory, offset, size};
+ hardware::cas::native::V1_0::DestinationBuffer dst;
+ if (secure) {
+ dst.type = BufferType::NATIVE_HANDLE;
+ dst.secureMemory = hardware::hidl_handle(block->handle());
+ } else {
+ dst.type = BufferType::SHARED_MEMORY;
+ dst.nonsecureMemory = src;
+ }
+
+ CasStatus status = CasStatus::OK;
+ hidl_string detailedError;
+ ScramblingControl sctrl = ScramblingControl::UNSCRAMBLED;
+
+ if (key != nullptr) {
+ sctrl = (ScramblingControl)key[0];
+ // Adjust for the PES offset
+ codecDataOffset = key[2] | (key[3] << 8);
+ }
+
+ auto returnVoid = mDescrambler->descramble(
+ sctrl,
+ hidlSubSamples,
+ src,
+ 0,
+ dst,
+ 0,
+ [&status, &result, &detailedError] (
+ CasStatus _status, uint32_t _bytesWritten,
+ const hidl_string& _detailedError) {
+ status = _status;
+ result = (ssize_t)_bytesWritten;
+ detailedError = _detailedError;
+ });
+
+ if (!returnVoid.isOk() || status != CasStatus::OK || result < 0) {
+ ALOGI("[%s] descramble failed, trans=%s, status=%d, result=%zd",
+ mName, returnVoid.description().c_str(), status, result);
+ return UNKNOWN_ERROR;
+ }
+
+ if (result < codecDataOffset) {
+ ALOGD("invalid codec data offset: %zd, result %zd", codecDataOffset, result);
+ return BAD_VALUE;
+ }
+ }
+ if (!secure) {
+ C2WriteView view = block->map().get();
+ if (view.error() != C2_OK) {
+ return UNKNOWN_ERROR;
+ }
+ if (view.size() < result) {
+ return UNKNOWN_ERROR;
+ }
+ memcpy(view.data(), mDecryptDestination->unsecurePointer(), result);
+ }
+ std::shared_ptr<C2Buffer> c2Buffer{C2Buffer::CreateLinearBuffer(
+ block->share(codecDataOffset, result - codecDataOffset, C2Fence{}))};
+ if (!buffer->copy(c2Buffer)) {
+ return -ENOSYS;
+ }
+ return OK;
+}
+
status_t CCodecBufferChannel::queueInputBuffer(const sp<MediaCodecBuffer> &buffer) {
QueueGuard guard(mSync);
if (!guard.isRunning()) {
@@ -774,7 +942,9 @@
}
status_t CCodecBufferChannel::start(
- const sp<AMessage> &inputFormat, const sp<AMessage> &outputFormat) {
+ const sp<AMessage> &inputFormat,
+ const sp<AMessage> &outputFormat,
+ bool buffersBoundToCodec) {
C2StreamBufferTypeSetting::input iStreamFormat(0u);
C2StreamBufferTypeSetting::output oStreamFormat(0u);
C2PortReorderBufferDepthTuning::output reorderDepth;
@@ -897,7 +1067,9 @@
input->numSlots = numInputSlots;
input->extraBuffers.flush();
input->numExtraSlots = 0u;
- if (graphic) {
+ if (!buffersBoundToCodec) {
+ input->buffers.reset(new SlotInputBuffers(mName));
+ } else if (graphic) {
if (mInputSurface) {
input->buffers.reset(new DummyInputBuffers(mName));
} else if (mMetaMode == MODE_ANW) {
@@ -1071,7 +1243,7 @@
output->outputDelay = outputDelayValue;
output->numSlots = numOutputSlots;
if (graphic) {
- if (outputSurface) {
+ if (outputSurface || !buffersBoundToCodec) {
output->buffers.reset(new GraphicOutputBuffers(mName));
} else {
output->buffers.reset(new RawGraphicOutputBuffers(numOutputSlots, mName));
@@ -1669,6 +1841,16 @@
}
void CCodecBufferChannel::setCrypto(const sp<ICrypto> &crypto) {
+ if (mCrypto != nullptr) {
+ for (std::pair<wp<HidlMemory>, int32_t> entry : mHeapSeqNumMap) {
+ mCrypto->unsetHeap(entry.second);
+ }
+ mHeapSeqNumMap.clear();
+ if (mHeapSeqNum >= 0) {
+ mCrypto->unsetHeap(mHeapSeqNum);
+ mHeapSeqNum = -1;
+ }
+ }
mCrypto = crypto;
}
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h
index 82fec18..0263211 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.h
+++ b/media/codec2/sfplugin/CCodecBufferChannel.h
@@ -70,6 +70,20 @@
const CryptoPlugin::SubSample *subSamples,
size_t numSubSamples,
AString *errorDetailMsg) override;
+ virtual status_t attachBuffer(
+ const std::shared_ptr<C2Buffer> &c2Buffer,
+ const sp<MediaCodecBuffer> &buffer) override;
+ virtual status_t attachEncryptedBuffer(
+ const sp<hardware::HidlMemory> &memory,
+ bool secure,
+ const uint8_t *key,
+ const uint8_t *iv,
+ CryptoPlugin::Mode mode,
+ CryptoPlugin::Pattern pattern,
+ size_t offset,
+ const CryptoPlugin::SubSample *subSamples,
+ size_t numSubSamples,
+ const sp<MediaCodecBuffer> &buffer) override;
virtual status_t renderOutputBuffer(
const sp<MediaCodecBuffer> &buffer, int64_t timestampNs) override;
virtual status_t discardBuffer(const sp<MediaCodecBuffer> &buffer) override;
@@ -108,7 +122,10 @@
* Start queueing buffers to the component. This object should never queue
* buffers before this call has completed.
*/
- status_t start(const sp<AMessage> &inputFormat, const sp<AMessage> &outputFormat);
+ status_t start(
+ const sp<AMessage> &inputFormat,
+ const sp<AMessage> &outputFormat,
+ bool buffersBoundToCodec);
/**
* Request initial input buffers to be filled by client.
@@ -216,11 +233,14 @@
std::unique_ptr<C2Work> work, const sp<AMessage> &outputFormat,
const C2StreamInitDataInfo::output *initData);
void sendOutputBuffers();
+ void ensureDecryptDestination(size_t size);
+ int32_t getHeapSeqNum(const sp<hardware::HidlMemory> &memory);
QueueSync mSync;
sp<MemoryDealer> mDealer;
sp<IMemory> mDecryptDestination;
int32_t mHeapSeqNum;
+ std::map<wp<hardware::HidlMemory>, int32_t> mHeapSeqNumMap;
std::shared_ptr<Codec2Client::Component> mComponent;
std::string mComponentName; ///< component name for debugging
diff --git a/media/codec2/sfplugin/CCodecBuffers.cpp b/media/codec2/sfplugin/CCodecBuffers.cpp
index ed8b832..265eeb7 100644
--- a/media/codec2/sfplugin/CCodecBuffers.cpp
+++ b/media/codec2/sfplugin/CCodecBuffers.cpp
@@ -22,6 +22,7 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MediaCodecConstants.h>
+#include <media/stagefright/SkipCutBuffer.h>
#include "CCodecBuffers.h"
@@ -493,6 +494,44 @@
return mAllocate();
}
+// SlotInputBuffers
+
+bool SlotInputBuffers::requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) {
+ sp<Codec2Buffer> newBuffer = createNewBuffer();
+ *index = mImpl.assignSlot(newBuffer);
+ *buffer = newBuffer;
+ return true;
+}
+
+bool SlotInputBuffers::releaseBuffer(
+ const sp<MediaCodecBuffer> &buffer,
+ std::shared_ptr<C2Buffer> *c2buffer,
+ bool release) {
+ return mImpl.releaseSlot(buffer, c2buffer, release);
+}
+
+bool SlotInputBuffers::expireComponentBuffer(
+ const std::shared_ptr<C2Buffer> &c2buffer) {
+ return mImpl.expireComponentBuffer(c2buffer);
+}
+
+void SlotInputBuffers::flush() {
+ mImpl.flush();
+}
+
+std::unique_ptr<InputBuffers> SlotInputBuffers::toArrayMode(size_t) {
+ TRESPASS("Array mode should not be called at non-legacy mode");
+ return nullptr;
+}
+
+size_t SlotInputBuffers::numClientBuffers() const {
+ return mImpl.numClientBuffers();
+}
+
+sp<Codec2Buffer> SlotInputBuffers::createNewBuffer() {
+ return new DummyContainerBuffer{mFormat, nullptr};
+}
+
// LinearInputBuffers
bool LinearInputBuffers::requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) {
diff --git a/media/codec2/sfplugin/CCodecBuffers.h b/media/codec2/sfplugin/CCodecBuffers.h
index 2cb6b81..bae08e0 100644
--- a/media/codec2/sfplugin/CCodecBuffers.h
+++ b/media/codec2/sfplugin/CCodecBuffers.h
@@ -25,10 +25,11 @@
#include <media/MediaCodecBuffer.h>
#include "Codec2Buffer.h"
-#include "SkipCutBuffer.h"
namespace android {
+class SkipCutBuffer;
+
constexpr size_t kLinearBufferSize = 1048576;
// This can fit 4K RGBA frame, and most likely client won't need more than this.
constexpr size_t kMaxLinearBufferSize = 4096 * 2304 * 4;
@@ -546,6 +547,36 @@
std::function<sp<Codec2Buffer>()> mAllocate;
};
+class SlotInputBuffers : public InputBuffers {
+public:
+ SlotInputBuffers(const char *componentName, const char *name = "Slot-Input")
+ : InputBuffers(componentName, name),
+ mImpl(mName) { }
+ ~SlotInputBuffers() override = default;
+
+ bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) final;
+
+ bool releaseBuffer(
+ const sp<MediaCodecBuffer> &buffer,
+ std::shared_ptr<C2Buffer> *c2buffer,
+ bool release) final;
+
+ bool expireComponentBuffer(
+ const std::shared_ptr<C2Buffer> &c2buffer) final;
+
+ void flush() final;
+
+ std::unique_ptr<InputBuffers> toArrayMode(size_t size) final;
+
+ size_t numClientBuffers() const final;
+
+protected:
+ sp<Codec2Buffer> createNewBuffer() final;
+
+private:
+ FlexBuffersImpl mImpl;
+};
+
class LinearInputBuffers : public InputBuffers {
public:
LinearInputBuffers(const char *componentName, const char *name = "1D-Input")
diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp
index ee3cdf6..d2f5ea7 100644
--- a/media/codec2/sfplugin/CCodecConfig.cpp
+++ b/media/codec2/sfplugin/CCodecConfig.cpp
@@ -595,34 +595,18 @@
.withMappers([](C2Value v) -> C2Value {
int32_t value;
if (v.get(&value)) {
- switch (value) {
- case COLOR_FormatSurface:
- return (uint32_t)HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
- case COLOR_FormatYUV420Flexible:
- return (uint32_t)HAL_PIXEL_FORMAT_YCBCR_420_888;
- case COLOR_FormatYUV420Planar:
- case COLOR_FormatYUV420SemiPlanar:
- case COLOR_FormatYUV420PackedPlanar:
- case COLOR_FormatYUV420PackedSemiPlanar:
- return (uint32_t)HAL_PIXEL_FORMAT_YV12;
- default:
- // TODO: support some sort of passthrough
- break;
+ uint32_t result;
+ if (C2Mapper::mapPixelFormatFrameworkToCodec(value, &result)) {
+ return result;
}
}
return C2Value();
}, [](C2Value v) -> C2Value {
uint32_t value;
if (v.get(&value)) {
- switch (value) {
- case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
- return COLOR_FormatSurface;
- case HAL_PIXEL_FORMAT_YV12:
- case HAL_PIXEL_FORMAT_YCBCR_420_888:
- return COLOR_FormatYUV420Flexible;
- default:
- // TODO: support some sort of passthrough
- break;
+ int32_t result;
+ if (C2Mapper::mapPixelFormatCodecToFramework(value, &result)) {
+ return result;
}
}
return C2Value();
diff --git a/media/codec2/sfplugin/CCodecConfig.h b/media/codec2/sfplugin/CCodecConfig.h
index a61c8b7..093bfdd 100644
--- a/media/codec2/sfplugin/CCodecConfig.h
+++ b/media/codec2/sfplugin/CCodecConfig.h
@@ -118,6 +118,7 @@
sp<AMessage> mOutputFormat;
bool mUsingSurface; ///< using input or output surface
+ bool mBuffersBoundToCodec; ///< whether buffers are bound to codecs or not.
std::shared_ptr<InputSurfaceWrapper> mInputSurface;
std::unique_ptr<InputSurfaceWrapper::Config> mISConfig;
diff --git a/media/codec2/sfplugin/Codec2Buffer.h b/media/codec2/sfplugin/Codec2Buffer.h
index 9291c52..09475ef 100644
--- a/media/codec2/sfplugin/Codec2Buffer.h
+++ b/media/codec2/sfplugin/Codec2Buffer.h
@@ -57,36 +57,6 @@
using MediaCodecBuffer::MediaCodecBuffer;
~Codec2Buffer() override = default;
- /**
- * \return C2Buffer object represents this buffer.
- */
- virtual std::shared_ptr<C2Buffer> asC2Buffer() = 0;
-
- /**
- * Test if we can copy the content of |buffer| into this object.
- *
- * \param buffer C2Buffer object to copy.
- * \return true if the content of buffer can be copied over to this buffer
- * false otherwise.
- */
- virtual bool canCopy(const std::shared_ptr<C2Buffer> &buffer) const {
- (void)buffer;
- return false;
- }
-
- /**
- * Copy the content of |buffer| into this object. This method assumes that
- * canCopy() check already passed.
- *
- * \param buffer C2Buffer object to copy.
- * \return true if successful
- * false otherwise.
- */
- virtual bool copy(const std::shared_ptr<C2Buffer> &buffer) {
- (void)buffer;
- return false;
- }
-
sp<ABuffer> getImageData() const { return mImageData; }
protected:
diff --git a/media/codec2/sfplugin/Codec2InfoBuilder.cpp b/media/codec2/sfplugin/Codec2InfoBuilder.cpp
index 745d701..5990116 100644
--- a/media/codec2/sfplugin/Codec2InfoBuilder.cpp
+++ b/media/codec2/sfplugin/Codec2InfoBuilder.cpp
@@ -43,13 +43,12 @@
#include <codec2/hidl/client.h>
#include <cutils/native_handle.h>
#include <media/omx/1.0/WOmxNode.h>
-#include <media/stagefright/MediaCodecConstants.h>
#include <media/stagefright/foundation/ALookup.h>
#include <media/stagefright/foundation/MediaDefs.h>
#include <media/stagefright/omx/OMXUtils.h>
#include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
-
-#include "Codec2InfoBuilder.h"
+#include <media/stagefright/Codec2InfoBuilder.h>
+#include <media/stagefright/MediaCodecConstants.h>
namespace android {
diff --git a/media/codec2/sfplugin/SkipCutBuffer.cpp b/media/codec2/sfplugin/SkipCutBuffer.cpp
deleted file mode 100644
index 8d1de65..0000000
--- a/media/codec2/sfplugin/SkipCutBuffer.cpp
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Copyright (C) 2012 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 "SkipCutBuffer"
-#include <utils/Log.h>
-
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/MediaBuffer.h>
-#include "SkipCutBuffer.h"
-
-namespace android {
-
-SkipCutBuffer::SkipCutBuffer(size_t skip, size_t cut, size_t num16BitChannels) {
-
- mWriteHead = 0;
- mReadHead = 0;
- mCapacity = 0;
- mCutBuffer = nullptr;
-
- if (num16BitChannels == 0 || num16BitChannels > INT32_MAX / 2) {
- ALOGW("# channels out of range: %zu, using passthrough instead", num16BitChannels);
- return;
- }
- size_t frameSize = num16BitChannels * 2;
- if (skip > INT32_MAX / frameSize || cut > INT32_MAX / frameSize
- || cut * frameSize > INT32_MAX - 4096) {
- ALOGW("out of range skip/cut: %zu/%zu, using passthrough instead",
- skip, cut);
- return;
- }
- skip *= frameSize;
- cut *= frameSize;
-
- mFrontPadding = mSkip = skip;
- mBackPadding = cut;
- mCapacity = cut + 4096;
- mCutBuffer = new (std::nothrow) char[mCapacity];
- ALOGV("skipcutbuffer %zu %zu %d", skip, cut, mCapacity);
-}
-
-SkipCutBuffer::~SkipCutBuffer() {
- delete[] mCutBuffer;
-}
-
-void SkipCutBuffer::submit(MediaBuffer *buffer) {
- if (mCutBuffer == nullptr) {
- // passthrough mode
- return;
- }
-
- int32_t offset = buffer->range_offset();
- int32_t buflen = buffer->range_length();
-
- // drop the initial data from the buffer if needed
- if (mFrontPadding > 0) {
- // still data left to drop
- int32_t to_drop = (buflen < mFrontPadding) ? buflen : mFrontPadding;
- offset += to_drop;
- buflen -= to_drop;
- buffer->set_range(offset, buflen);
- mFrontPadding -= to_drop;
- }
-
-
- // append data to cutbuffer
- char *src = ((char*) buffer->data()) + offset;
- write(src, buflen);
-
-
- // the mediabuffer is now empty. Fill it from cutbuffer, always leaving
- // at least mBackPadding bytes in the cutbuffer
- char *dst = (char*) buffer->data();
- size_t copied = read(dst, buffer->size());
- buffer->set_range(0, copied);
-}
-
-template <typename T>
-void SkipCutBuffer::submitInternal(const sp<T>& buffer) {
- if (mCutBuffer == nullptr) {
- // passthrough mode
- return;
- }
-
- int32_t offset = buffer->offset();
- int32_t buflen = buffer->size();
-
- // drop the initial data from the buffer if needed
- if (mFrontPadding > 0) {
- // still data left to drop
- int32_t to_drop = (buflen < mFrontPadding) ? buflen : mFrontPadding;
- offset += to_drop;
- buflen -= to_drop;
- buffer->setRange(offset, buflen);
- mFrontPadding -= to_drop;
- }
-
-
- // append data to cutbuffer
- char *src = (char*) buffer->data();
- write(src, buflen);
-
-
- // the mediabuffer is now empty. Fill it from cutbuffer, always leaving
- // at least mBackPadding bytes in the cutbuffer
- char *dst = (char*) buffer->base();
- size_t copied = read(dst, buffer->capacity());
- buffer->setRange(0, copied);
-}
-
-void SkipCutBuffer::submit(const sp<ABuffer>& buffer) {
- submitInternal(buffer);
-}
-
-void SkipCutBuffer::submit(const sp<MediaCodecBuffer>& buffer) {
- submitInternal(buffer);
-}
-
-void SkipCutBuffer::clear() {
- mWriteHead = mReadHead = 0;
- mFrontPadding = mSkip;
-}
-
-void SkipCutBuffer::write(const char *src, size_t num) {
- int32_t sizeused = (mWriteHead - mReadHead);
- if (sizeused < 0) sizeused += mCapacity;
-
- // Everything must fit. Make sure the buffer is a little larger than needed,
- // so there is no ambiguity as to whether mWriteHead == mReadHead means buffer
- // full or empty
- size_t available = mCapacity - sizeused - 32;
- if (available < num) {
- int32_t newcapacity = mCapacity + (num - available);
- char * newbuffer = new char[newcapacity];
- memcpy(newbuffer, mCutBuffer, mCapacity);
- delete [] mCutBuffer;
- mCapacity = newcapacity;
- mCutBuffer = newbuffer;
- ALOGV("reallocated buffer at size %d", newcapacity);
- }
-
- size_t copyfirst = (mCapacity - mWriteHead);
- if (copyfirst > num) copyfirst = num;
- if (copyfirst) {
- memcpy(mCutBuffer + mWriteHead, src, copyfirst);
- num -= copyfirst;
- src += copyfirst;
- mWriteHead += copyfirst;
- CHECK_LE(mWriteHead, mCapacity);
- if (mWriteHead == mCapacity) mWriteHead = 0;
- if (num) {
- memcpy(mCutBuffer, src, num);
- mWriteHead += num;
- }
- }
-}
-
-size_t SkipCutBuffer::read(char *dst, size_t num) {
- int32_t available = (mWriteHead - mReadHead);
- if (available < 0) available += mCapacity;
-
- available -= mBackPadding;
- if (available <=0) {
- return 0;
- }
- if (available < int32_t(num)) {
- num = available;
- }
-
- size_t copyfirst = (mCapacity - mReadHead);
- if (copyfirst > num) copyfirst = num;
- if (copyfirst) {
- memcpy(dst, mCutBuffer + mReadHead, copyfirst);
- num -= copyfirst;
- dst += copyfirst;
- mReadHead += copyfirst;
- CHECK_LE(mReadHead, mCapacity);
- if (mReadHead == mCapacity) mReadHead = 0;
- if (num) {
- memcpy(dst, mCutBuffer, num);
- mReadHead += num;
- }
- }
- return available;
-}
-
-size_t SkipCutBuffer::size() {
- int32_t available = (mWriteHead - mReadHead);
- if (available < 0) available += mCapacity;
- return available;
-}
-
-} // namespace android
diff --git a/media/codec2/sfplugin/SkipCutBuffer.h b/media/codec2/sfplugin/SkipCutBuffer.h
deleted file mode 100644
index 0fb5690..0000000
--- a/media/codec2/sfplugin/SkipCutBuffer.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef SKIP_CUT_BUFFER_H_
-
-#define SKIP_CUT_BUFFER_H_
-
-#include <media/MediaCodecBuffer.h>
-#include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/foundation/ABuffer.h>
-
-namespace android {
-
-/**
- * utility class to cut the start and end off a stream of data in MediaBuffers
- *
- */
-class SkipCutBuffer: public RefBase {
- public:
- // 'skip' is the number of frames to skip from the beginning
- // 'cut' is the number of frames to cut from the end
- // 'num16BitChannels' is the number of channels, which are assumed to be 16 bit wide each
- SkipCutBuffer(size_t skip, size_t cut, size_t num16Channels);
-
- // Submit one MediaBuffer for skipping and cutting. This may consume all or
- // some of the data in the buffer, or it may add data to it.
- // After this, the caller should continue processing the buffer as usual.
- void submit(MediaBuffer *buffer);
- void submit(const sp<ABuffer>& buffer); // same as above, but with an ABuffer
- void submit(const sp<MediaCodecBuffer>& buffer); // same as above, but with an ABuffer
- void clear();
- size_t size(); // how many bytes are currently stored in the buffer
-
- protected:
- virtual ~SkipCutBuffer();
-
- private:
- void write(const char *src, size_t num);
- size_t read(char *dst, size_t num);
- template <typename T>
- void submitInternal(const sp<T>& buffer);
- int32_t mSkip;
- int32_t mFrontPadding;
- int32_t mBackPadding;
- int32_t mWriteHead;
- int32_t mReadHead;
- int32_t mCapacity;
- char* mCutBuffer;
- DISALLOW_EVIL_CONSTRUCTORS(SkipCutBuffer);
-};
-
-} // namespace android
-
-#endif // OMX_CODEC_H_
diff --git a/media/codec2/sfplugin/CCodec.h b/media/codec2/sfplugin/include/media/stagefright/CCodec.h
similarity index 87%
rename from media/codec2/sfplugin/CCodec.h
rename to media/codec2/sfplugin/include/media/stagefright/CCodec.h
index b0b3c4f..6ff2c4a 100644
--- a/media/codec2/sfplugin/CCodec.h
+++ b/media/codec2/sfplugin/include/media/stagefright/CCodec.h
@@ -36,12 +36,11 @@
#include <hardware/gralloc.h>
#include <nativebase/nativebase.h>
-#include "CCodecConfig.h"
-
namespace android {
class CCodecBufferChannel;
class InputSurfaceWrapper;
+struct CCodecConfig;
struct MediaCodecInfo;
class CCodec : public CodecBase {
@@ -69,6 +68,24 @@
void onWorkDone(std::list<std::unique_ptr<C2Work>> &workItems);
void onInputBufferDone(uint64_t frameIndex, size_t arrayIndex);
+ static PersistentSurface *CreateInputSurface();
+
+ static status_t CanFetchLinearBlock(
+ const std::vector<std::string> &names, const C2MemoryUsage &usage, bool *isCompatible);
+
+ static std::shared_ptr<C2LinearBlock> FetchLinearBlock(
+ size_t capacity, const C2MemoryUsage &usage, const std::vector<std::string> &names);
+
+ static status_t CanFetchGraphicBlock(
+ const std::vector<std::string> &names, bool *isCompatible);
+
+ static std::shared_ptr<C2GraphicBlock> FetchGraphicBlock(
+ int32_t width,
+ int32_t height,
+ int32_t format,
+ uint64_t usage,
+ const std::vector<std::string> &names);
+
protected:
virtual ~CCodec();
@@ -173,7 +190,7 @@
Mutexed<NamedTimePoint> mDeadline;
typedef CCodecConfig Config;
- Mutexed<Config> mConfig;
+ Mutexed<std::unique_ptr<CCodecConfig>> mConfig;
Mutexed<std::list<std::unique_ptr<C2Work>>> mWorkDoneQueue;
friend class CCodecCallbackImpl;
diff --git a/media/codec2/sfplugin/Codec2InfoBuilder.h b/media/codec2/sfplugin/include/media/stagefright/Codec2InfoBuilder.h
similarity index 100%
rename from media/codec2/sfplugin/Codec2InfoBuilder.h
rename to media/codec2/sfplugin/include/media/stagefright/Codec2InfoBuilder.h
diff --git a/media/codec2/sfplugin/tests/Android.bp b/media/codec2/sfplugin/tests/Android.bp
index b6eb2b4..c953f84 100644
--- a/media/codec2/sfplugin/tests/Android.bp
+++ b/media/codec2/sfplugin/tests/Android.bp
@@ -35,6 +35,7 @@
header_libs: [
"libmediadrm_headers",
+ "libmediametrics_headers",
],
shared_libs: [
diff --git a/media/codec2/sfplugin/utils/Android.bp b/media/codec2/sfplugin/utils/Android.bp
index 8c8f025..205abdc 100644
--- a/media/codec2/sfplugin/utils/Android.bp
+++ b/media/codec2/sfplugin/utils/Android.bp
@@ -1,6 +1,7 @@
cc_library_shared {
name: "libsfplugin_ccodec_utils",
vendor_available: true,
+ double_loadable: true,
srcs: [
"Codec2BufferUtils.cpp",
diff --git a/media/codec2/sfplugin/utils/Codec2Mapper.cpp b/media/codec2/sfplugin/utils/Codec2Mapper.cpp
index 2f3d688..903db6c 100644
--- a/media/codec2/sfplugin/utils/Codec2Mapper.cpp
+++ b/media/codec2/sfplugin/utils/Codec2Mapper.cpp
@@ -949,3 +949,41 @@
bool C2Mapper::map(ColorAspects::Transfer from, C2Color::transfer_t *to) {
return sColorTransfersSf.map(from, to);
}
+
+// static
+bool C2Mapper::mapPixelFormatFrameworkToCodec(
+ int32_t frameworkValue, uint32_t *c2Value) {
+ switch (frameworkValue) {
+ case COLOR_FormatSurface:
+ *c2Value = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
+ return true;
+ case COLOR_FormatYUV420Flexible:
+ *c2Value = HAL_PIXEL_FORMAT_YCBCR_420_888;
+ return true;
+ case COLOR_FormatYUV420Planar:
+ case COLOR_FormatYUV420SemiPlanar:
+ case COLOR_FormatYUV420PackedPlanar:
+ case COLOR_FormatYUV420PackedSemiPlanar:
+ *c2Value = HAL_PIXEL_FORMAT_YV12;
+ return true;
+ default:
+ // TODO: support some sort of passthrough
+ return false;
+ }
+}
+
+// static
+bool C2Mapper::mapPixelFormatCodecToFramework(
+ uint32_t c2Value, int32_t *frameworkValue) {
+ switch (c2Value) {
+ case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+ *frameworkValue = COLOR_FormatSurface;
+ return true;
+ case HAL_PIXEL_FORMAT_YV12:
+ case HAL_PIXEL_FORMAT_YCBCR_420_888:
+ *frameworkValue = COLOR_FormatYUV420Flexible;
+ return true;
+ default:
+ return false;
+ }
+}
diff --git a/media/codec2/sfplugin/utils/Codec2Mapper.h b/media/codec2/sfplugin/utils/Codec2Mapper.h
index cec6f07..797c8a8 100644
--- a/media/codec2/sfplugin/utils/Codec2Mapper.h
+++ b/media/codec2/sfplugin/utils/Codec2Mapper.h
@@ -75,6 +75,11 @@
static bool map(ColorAspects::MatrixCoeffs, C2Color::matrix_t*);
static bool map(C2Color::transfer_t, ColorAspects::Transfer*);
static bool map(ColorAspects::Transfer, C2Color::transfer_t*);
+
+ static bool mapPixelFormatFrameworkToCodec(
+ int32_t frameworkValue, uint32_t *c2Value);
+ static bool mapPixelFormatCodecToFramework(
+ uint32_t c2Value, int32_t *frameworkValue);
};
}
diff --git a/media/codec2/vndk/Android.bp b/media/codec2/vndk/Android.bp
index 071bb74..a1f145b 100644
--- a/media/codec2/vndk/Android.bp
+++ b/media/codec2/vndk/Android.bp
@@ -14,6 +14,8 @@
cc_library_shared {
name: "libcodec2_vndk",
vendor_available: true,
+ // TODO: b/147147883
+ double_loadable: true,
srcs: [
"C2AllocatorBlob.cpp",
@@ -68,6 +70,8 @@
"libutils",
],
+ tidy: false, // b/146435095, clang-tidy segmentation fault
+
cflags: [
"-Werror",
"-Wall",
diff --git a/media/codec2/vndk/C2AllocatorIon.cpp b/media/codec2/vndk/C2AllocatorIon.cpp
index 0470a31..dfc90b1 100644
--- a/media/codec2/vndk/C2AllocatorIon.cpp
+++ b/media/codec2/vndk/C2AllocatorIon.cpp
@@ -262,7 +262,7 @@
*fence = C2Fence(); // not using fences
}
(void)mMappings.erase(it);
- ALOGV("successfully unmapped: %d", mHandle.bufferFd());
+ ALOGV("successfully unmapped: addr=%p size=%zu fd=%d", addr, size, mHandle.bufferFd());
return C2_OK;
}
ALOGD("unmap failed to find specified map");
diff --git a/media/extractors/Android.bp b/media/extractors/Android.bp
new file mode 100644
index 0000000..bb42580
--- /dev/null
+++ b/media/extractors/Android.bp
@@ -0,0 +1,46 @@
+// 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.
+
+cc_defaults {
+ name: "extractor-defaults",
+
+ include_dirs: [
+ "frameworks/av/media/libstagefright/include",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libmediandk#29",
+ ],
+
+ relative_install_path: "extractors",
+
+ compile_multilib: "first",
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ "-fvisibility=hidden",
+ ],
+
+ version_script: "exports.lds",
+
+ sanitize: {
+ cfi: true,
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ },
+}
\ No newline at end of file
diff --git a/media/extractors/aac/Android.bp b/media/extractors/aac/Android.bp
index a58167a..60d3ae1 100644
--- a/media/extractors/aac/Android.bp
+++ b/media/extractors/aac/Android.bp
@@ -1,40 +1,13 @@
-cc_library_shared {
+cc_library {
+ name: "libaacextractor",
+ defaults: ["extractor-defaults"],
srcs: ["AACExtractor.cpp"],
- include_dirs: [
- "frameworks/av/media/libstagefright/",
- ],
-
- shared_libs: [
- "liblog",
- "libmediandk",
- ],
-
static_libs: [
"libstagefright_foundation",
"libstagefright_metadatautils",
"libutils",
],
- name: "libaacextractor",
- relative_install_path: "extractors",
-
- compile_multilib: "first",
-
- cflags: [
- "-Werror",
- "-Wall",
- "-fvisibility=hidden",
- ],
- version_script: "exports.lds",
-
- sanitize: {
- cfi: true,
- misc_undefined: [
- "unsigned-integer-overflow",
- "signed-integer-overflow",
- ],
- },
-
}
diff --git a/media/extractors/amr/Android.bp b/media/extractors/amr/Android.bp
index 4bd933d..49c9567 100644
--- a/media/extractors/amr/Android.bp
+++ b/media/extractors/amr/Android.bp
@@ -1,38 +1,11 @@
-cc_library_shared {
+cc_library {
+ name: "libamrextractor",
+ defaults: ["extractor-defaults"],
srcs: ["AMRExtractor.cpp"],
- include_dirs: [
- "frameworks/av/media/libstagefright/include",
- ],
-
- shared_libs: [
- "liblog",
- "libmediandk",
- ],
-
static_libs: [
"libstagefright_foundation",
],
- name: "libamrextractor",
- relative_install_path: "extractors",
-
- compile_multilib: "first",
-
- cflags: [
- "-Werror",
- "-Wall",
- "-fvisibility=hidden",
- ],
- version_script: "exports.lds",
-
- sanitize: {
- cfi: true,
- misc_undefined: [
- "unsigned-integer-overflow",
- "signed-integer-overflow",
- ],
- },
-
}
diff --git a/media/extractors/flac/Android.bp b/media/extractors/flac/Android.bp
index 3a3d051..3675611 100644
--- a/media/extractors/flac/Android.bp
+++ b/media/extractors/flac/Android.bp
@@ -1,16 +1,15 @@
-cc_library_shared {
+cc_library {
+ name: "libflacextractor",
+ defaults: ["extractor-defaults"],
srcs: ["FLACExtractor.cpp"],
include_dirs: [
- "frameworks/av/media/libstagefright/include",
"external/flac/include",
],
shared_libs: [
"libbinder_ndk",
- "liblog",
- "libmediandk",
],
static_libs: [
@@ -21,24 +20,4 @@
"libutils",
],
- name: "libflacextractor",
- relative_install_path: "extractors",
-
- compile_multilib: "first",
-
- cflags: [
- "-Werror",
- "-Wall",
- "-fvisibility=hidden",
- ],
- version_script: "exports.lds",
-
- sanitize: {
- cfi: true,
- misc_undefined: [
- "unsigned-integer-overflow",
- "signed-integer-overflow",
- ],
- },
-
}
diff --git a/media/extractors/midi/Android.bp b/media/extractors/midi/Android.bp
index d36cb49..592ffa9 100644
--- a/media/extractors/midi/Android.bp
+++ b/media/extractors/midi/Android.bp
@@ -1,43 +1,18 @@
-cc_library_shared {
+cc_library {
+ name: "libmidiextractor",
+ defaults: ["extractor-defaults"],
srcs: ["MidiExtractor.cpp"],
- include_dirs: [
- "frameworks/av/media/libstagefright/include",
- ],
-
header_libs: [
"libmedia_headers",
],
- shared_libs: [
- "liblog",
- "libmediandk",
- ],
-
static_libs: [
"libmedia_midiiowrapper",
"libsonivox",
"libstagefright_foundation"
],
- name: "libmidiextractor",
- relative_install_path: "extractors",
- compile_multilib: "first",
-
- cflags: [
- "-Werror",
- "-Wall",
- "-fvisibility=hidden",
- ],
- version_script: "exports.lds",
-
- sanitize: {
- cfi: true,
- misc_undefined: [
- "unsigned-integer-overflow",
- "signed-integer-overflow",
- ],
- },
}
diff --git a/media/extractors/mkv/Android.bp b/media/extractors/mkv/Android.bp
index 38821fd..7ad8cc1 100644
--- a/media/extractors/mkv/Android.bp
+++ b/media/extractors/mkv/Android.bp
@@ -1,4 +1,6 @@
-cc_library_shared {
+cc_library {
+ name: "libmkvextractor",
+ defaults: ["extractor-defaults"],
srcs: ["MatroskaExtractor.cpp"],
@@ -6,12 +8,9 @@
"external/flac/include",
"external/libvpx/libwebm",
"frameworks/av/media/libstagefright/flac/dec",
- "frameworks/av/media/libstagefright/include",
],
shared_libs: [
- "liblog",
- "libmediandk",
"libstagefright_flacdec",
],
@@ -22,24 +21,4 @@
"libutils",
],
- name: "libmkvextractor",
- relative_install_path: "extractors",
-
- compile_multilib: "first",
-
- cflags: [
- "-Werror",
- "-Wall",
- "-fvisibility=hidden",
- ],
- version_script: "exports.lds",
-
- sanitize: {
- cfi: true,
- misc_undefined: [
- "unsigned-integer-overflow",
- "signed-integer-overflow",
- ],
- },
-
}
diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp
index 23022e4..044c4d0 100644
--- a/media/extractors/mkv/MatroskaExtractor.cpp
+++ b/media/extractors/mkv/MatroskaExtractor.cpp
@@ -22,7 +22,7 @@
#include "MatroskaExtractor.h"
#include "common/webmids.h"
-#include <media/DataSourceBase.h>
+#include <media/stagefright/DataSourceBase.h>
#include <media/ExtractorUtils.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AUtils.h>
diff --git a/media/extractors/mp3/Android.bp b/media/extractors/mp3/Android.bp
index 4e2f248..102ac81 100644
--- a/media/extractors/mp3/Android.bp
+++ b/media/extractors/mp3/Android.bp
@@ -1,44 +1,16 @@
-cc_library_shared {
-
+cc_library {
+ name: "libmp3extractor",
+ defaults: ["extractor-defaults"],
srcs: [
"MP3Extractor.cpp",
"VBRISeeker.cpp",
"XINGSeeker.cpp",
],
- include_dirs: [
- "frameworks/av/media/libstagefright/include",
- ],
-
- shared_libs: [
- "liblog",
- "libmediandk",
- ],
-
static_libs: [
"libutils",
"libstagefright_id3",
"libstagefright_foundation",
],
- name: "libmp3extractor",
- relative_install_path: "extractors",
-
- compile_multilib: "first",
-
- cflags: [
- "-Werror",
- "-Wall",
- "-fvisibility=hidden",
- ],
- version_script: "exports.lds",
-
- sanitize: {
- cfi: true,
- misc_undefined: [
- "unsigned-integer-overflow",
- "signed-integer-overflow",
- ],
- },
-
}
diff --git a/media/extractors/mp4/Android.bp b/media/extractors/mp4/Android.bp
index 1b308aa..e48e1b7 100644
--- a/media/extractors/mp4/Android.bp
+++ b/media/extractors/mp4/Android.bp
@@ -1,5 +1,6 @@
-cc_defaults {
- name: "libmp4extractor_defaults",
+cc_library {
+ name: "libmp4extractor",
+ defaults: ["extractor-defaults"],
srcs: [
"AC4Parser.cpp",
@@ -9,50 +10,10 @@
"SampleTable.cpp",
],
- include_dirs: [
- "frameworks/av/media/libstagefright/",
- ],
-
- shared_libs: [
- "liblog",
- "libmediandk"
- ],
-
static_libs: [
"libstagefright_esds",
"libstagefright_foundation",
"libstagefright_id3",
"libutils",
],
-
- cflags: [
- "-Werror",
- "-Wall",
- "-fvisibility=hidden",
- ],
- version_script: "exports.lds",
- relative_install_path: "extractors",
- compile_multilib: "first",
-}
-
-cc_library_shared {
-
-
- name: "libmp4extractor",
- defaults: ["libmp4extractor_defaults"],
-
- sanitize: {
- cfi: true,
- misc_undefined: [
- "unsigned-integer-overflow",
- "signed-integer-overflow",
- ],
- },
-
-}
-
-cc_library_static {
- name: "libmp4extractor_fuzzing",
-
- defaults: ["libmp4extractor_defaults"],
}
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index 6f85960..ce82861 100755
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -31,9 +31,10 @@
#include "MPEG4Extractor.h"
#include "SampleTable.h"
#include "ItemTable.h"
-#include "include/ESDS.h"
-#include <media/DataSourceBase.h>
+#include <ESDS.h>
+#include <ID3.h>
+#include <media/stagefright/DataSourceBase.h>
#include <media/ExtractorUtils.h>
#include <media/stagefright/foundation/ABitReader.h>
#include <media/stagefright/foundation/ABuffer.h>
@@ -52,7 +53,6 @@
#include <utils/String8.h>
#include <byteswap.h>
-#include "include/ID3.h"
#ifndef UINT32_MAX
#define UINT32_MAX (4294967295U)
@@ -83,7 +83,8 @@
const Trex *trex,
off64_t firstMoofOffset,
const sp<ItemTable> &itemTable,
- uint64_t elstShiftStartTicks);
+ uint64_t elstShiftStartTicks,
+ uint64_t elstInitialEmptyEditTicks);
virtual status_t init();
virtual media_status_t start();
@@ -151,6 +152,7 @@
// Start offset from composition time to presentation time.
// Support shift only for video tracks through mElstShiftStartTicks for now.
uint64_t mElstShiftStartTicks;
+ uint64_t mElstInitialEmptyEditTicks;
size_t parseNALSize(const uint8_t *data) const;
status_t parseChunk(off64_t *offset);
@@ -484,12 +486,11 @@
int64_t duration;
int32_t samplerate;
// Only for audio track.
- if (track->has_elst && mHeaderTimescale != 0 &&
- AMediaFormat_getInt64(track->meta, AMEDIAFORMAT_KEY_DURATION, &duration) &&
- AMediaFormat_getInt32(track->meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, &samplerate)) {
-
+ if (track->elst_needs_processing && mHeaderTimescale != 0 &&
+ AMediaFormat_getInt64(track->meta, AMEDIAFORMAT_KEY_DURATION, &duration) &&
+ AMediaFormat_getInt32(track->meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, &samplerate)) {
// Elst has to be processed only the first time this function is called.
- track->has_elst = false;
+ track->elst_needs_processing = false;
if (track->elst_segment_duration > INT64_MAX) {
return;
@@ -1098,10 +1099,11 @@
track_b->sampleTable = mLastTrack->sampleTable;
track_b->includes_expensive_metadata = mLastTrack->includes_expensive_metadata;
track_b->skipTrack = mLastTrack->skipTrack;
- track_b->has_elst = mLastTrack->has_elst;
+ track_b->elst_needs_processing = mLastTrack->elst_needs_processing;
track_b->elst_media_time = mLastTrack->elst_media_time;
track_b->elst_segment_duration = mLastTrack->elst_segment_duration;
- track_b->elstShiftStartTicks = mLastTrack->elstShiftStartTicks;
+ track_b->elst_shift_start_ticks = mLastTrack->elst_shift_start_ticks;
+ track_b->elst_initial_empty_edit_ticks = mLastTrack->elst_initial_empty_edit_ticks;
track_b->subsample_encryption = mLastTrack->subsample_encryption;
track_b->mTx3gBuffer = mLastTrack->mTx3gBuffer;
@@ -1204,39 +1206,86 @@
return ERROR_IO;
}
- if (entry_count != 1) {
- // we only support a single entry at the moment, for gapless playback
- // or start offset
+ if (entry_count > 2) {
+ /* We support a single entry for gapless playback or negating offset for
+ * reordering B frames, two entries (empty edit) for start offset at the moment.
+ */
ALOGW("ignoring edit list with %d entries", entry_count);
} else {
off64_t entriesoffset = data_offset + 8;
uint64_t segment_duration;
int64_t media_time;
-
- if (version == 1) {
- if (!mDataSource->getUInt64(entriesoffset, &segment_duration) ||
- !mDataSource->getUInt64(entriesoffset + 8, (uint64_t*)&media_time)) {
- return ERROR_IO;
- }
- } else if (version == 0) {
- uint32_t sd;
- int32_t mt;
- if (!mDataSource->getUInt32(entriesoffset, &sd) ||
+ uint64_t empty_edit_ticks = 0;
+ bool empty_edit_present = false;
+ for (int i = 0; i < entry_count; ++i) {
+ switch (version) {
+ case 0: {
+ uint32_t sd;
+ int32_t mt;
+ if (!mDataSource->getUInt32(entriesoffset, &sd) ||
!mDataSource->getUInt32(entriesoffset + 4, (uint32_t*)&mt)) {
- return ERROR_IO;
+ return ERROR_IO;
+ }
+ segment_duration = sd;
+ media_time = mt;
+ // 4(segment duration) + 4(media time) + 4(media rate)
+ entriesoffset += 12;
+ break;
}
- segment_duration = sd;
- media_time = mt;
- } else {
- return ERROR_IO;
+ case 1: {
+ if (!mDataSource->getUInt64(entriesoffset, &segment_duration) ||
+ !mDataSource->getUInt64(entriesoffset + 8, (uint64_t*)&media_time)) {
+ return ERROR_IO;
+ }
+ // 8(segment duration) + 8(media time) + 4(media rate)
+ entriesoffset += 20;
+ break;
+ }
+ default:
+ return ERROR_IO;
+ break;
+ }
+ // Empty edit entry would have to be first entry.
+ if (media_time == -1 && i == 0) {
+ int64_t durationUs;
+ if (AMediaFormat_getInt64(mFileMetaData, AMEDIAFORMAT_KEY_DURATION,
+ &durationUs)) {
+ empty_edit_ticks = segment_duration;
+ ALOGV("initial empty edit ticks: %" PRIu64, empty_edit_ticks);
+ empty_edit_present = true;
+ }
+ }
+ // Process second entry only when the first entry was an empty edit entry.
+ if (empty_edit_present && i == 1) {
+ int64_t durationUs;
+ if (AMediaFormat_getInt64(mLastTrack->meta, AMEDIAFORMAT_KEY_DURATION,
+ &durationUs) &&
+ mHeaderTimescale != 0) {
+ // Support only segment_duration<=track_duration and media_time==0 case.
+ uint64_t segmentDurationUs =
+ segment_duration * 1000000 / mHeaderTimescale;
+ if (segmentDurationUs == 0 || segmentDurationUs > durationUs ||
+ media_time != 0) {
+ ALOGW("for now, unsupported second entry in empty edit list");
+ }
+ }
+ }
}
-
// save these for later, because the elst atom might precede
// the atoms that actually gives us the duration and sample rate
// needed to calculate the padding and delay values
- mLastTrack->has_elst = true;
- mLastTrack->elst_media_time = media_time;
- mLastTrack->elst_segment_duration = segment_duration;
+ mLastTrack->elst_needs_processing = true;
+ if (empty_edit_present) {
+ /* In movie header timescale, and needs to be converted to media timescale once
+ * we get that from a track's 'mdhd' atom, which at times come after 'elst'.
+ */
+ mLastTrack->elst_initial_empty_edit_ticks = empty_edit_ticks;
+ } else {
+ mLastTrack->elst_media_time = media_time;
+ mLastTrack->elst_segment_duration = segment_duration;
+ ALOGV("segment_duration: %" PRIu64 " media_time: %" PRId64, segment_duration,
+ media_time);
+ }
}
break;
}
@@ -1631,8 +1680,12 @@
{
*offset += chunk_size;
- if (mLastTrack == NULL)
+ // the absolute minimum size of a compliant mett box is 11 bytes:
+ // 6 byte reserved, 2 byte index, null byte, one char mime_format, null byte
+ // The resulting mime_format would be invalid at that size though.
+ if (mLastTrack == NULL || chunk_data_size < 11) {
return ERROR_MALFORMED;
+ }
auto buffer = heapbuffer<uint8_t>(chunk_data_size);
if (buffer.get() == NULL) {
@@ -1644,10 +1697,24 @@
return ERROR_IO;
}
+ // ISO-14496-12:
+ // int8 reserved[6]; // should be all zeroes
+ // int16_t data_reference_index;
+ // char content_encoding[]; // null terminated, optional (= just the null byte)
+ // char mime_format[]; // null terminated, mandatory
+ // optional other boxes
+ //
+ // API < 29:
+ // char mime_format[]; // null terminated
+ //
+ // API >= 29
+ // char mime_format[]; // null terminated
+ // char mime_format[]; // null terminated
+
// Prior to API 29, the metadata track was not compliant with ISO/IEC
// 14496-12-2015. This led to some ISO-compliant parsers failing to read the
// metatrack. As of API 29 and onwards, a change was made to metadata track to
- // make it compliant with the standard. The workaround is to write the
+ // make it somewhat compatible with the standard. The workaround is to write the
// null-terminated mime_format string twice. This allows compliant parsers to
// read the missing reserved, data_reference_index, and content_encoding fields
// from the first mime_type string. The actual mime_format field would then be
@@ -1656,27 +1723,27 @@
// as it would only read the first null-terminated mime_format string. To enable
// reading metadata tracks generated from both the non-compliant and compliant
// formats, a check needs to be done to see which format is used.
- int null_pos = 0;
- const unsigned char *str = buffer.get();
- while (null_pos < chunk_data_size) {
- if (*(str + null_pos) == '\0') {
- break;
- }
- ++null_pos;
- }
+ const char *str = (const char*) buffer.get();
+ size_t string_length = strnlen(str, chunk_data_size);
- if (null_pos == chunk_data_size - 1) {
- // This is not a standard ompliant metadata track.
- String8 mimeFormat((const char *)(buffer.get()), chunk_data_size);
- AMediaFormat_setString(mLastTrack->meta,
- AMEDIAFORMAT_KEY_MIME, mimeFormat.string());
+ if (string_length == chunk_data_size - 1) {
+ // This is likely a pre API 29 file, since it's a single null terminated
+ // string filling the entire box.
+ AMediaFormat_setString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, str);
} else {
- // This is a standard compliant metadata track.
- String8 contentEncoding((const char *)(buffer.get() + 8));
- String8 mimeFormat((const char *)(buffer.get() + 8 + contentEncoding.size() + 1),
- chunk_data_size - 8 - contentEncoding.size() - 1);
- AMediaFormat_setString(mLastTrack->meta,
- AMEDIAFORMAT_KEY_MIME, mimeFormat.string());
+ // This might be a fully compliant metadata track, a "double mime" compatibility
+ // track, or anything else, including a single non-terminated string, so we need
+ // to determine the length of each string we want to parse out of the box.
+ size_t encoding_length = strnlen(str + 8, chunk_data_size - 8);
+ if (encoding_length + 8 >= chunk_data_size - 2) {
+ // the encoding extends to the end of the box, so there's no mime_format
+ return ERROR_MALFORMED;
+ }
+ String8 contentEncoding(str + 8, encoding_length);
+ String8 mimeFormat(str + 8 + encoding_length + 1,
+ chunk_data_size - 8 - encoding_length - 1);
+ AMediaFormat_setString(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_MIME, mimeFormat.string());
}
break;
}
@@ -4257,15 +4324,28 @@
}
}
- if (track->has_elst and !strncasecmp("video/", mime, 6) and track->elst_media_time > 0) {
- track->elstShiftStartTicks = track->elst_media_time;
- ALOGV("video track->elstShiftStartTicks :%" PRIu64, track->elstShiftStartTicks);
+ // media_time is in media timescale as are STTS/CTTS entries.
+ track->elst_shift_start_ticks = track->elst_media_time;
+ ALOGV("track->elst_shift_start_ticks :%" PRIu64, track->elst_shift_start_ticks);
+ if (mHeaderTimescale != 0) {
+ // Convert empty_edit_ticks from movie timescale to media timescale.
+ uint64_t elst_initial_empty_edit_ticks_mul = 0, elst_initial_empty_edit_ticks_add = 0;
+ if (__builtin_mul_overflow(track->elst_initial_empty_edit_ticks, track->timescale,
+ &elst_initial_empty_edit_ticks_mul) ||
+ __builtin_add_overflow(elst_initial_empty_edit_ticks_mul, (mHeaderTimescale / 2),
+ &elst_initial_empty_edit_ticks_add)) {
+ ALOGE("track->elst_initial_empty_edit_ticks overflow");
+ return nullptr;
+ }
+ track->elst_initial_empty_edit_ticks = elst_initial_empty_edit_ticks_add / mHeaderTimescale;
+ ALOGV("track->elst_initial_empty_edit_ticks :%" PRIu64,
+ track->elst_initial_empty_edit_ticks);
}
- MPEG4Source *source = new MPEG4Source(
- track->meta, mDataSource, track->timescale, track->sampleTable,
- mSidxEntries, trex, mMoofOffset, itemTable,
- track->elstShiftStartTicks);
+ MPEG4Source* source =
+ new MPEG4Source(track->meta, mDataSource, track->timescale, track->sampleTable,
+ mSidxEntries, trex, mMoofOffset, itemTable,
+ track->elst_shift_start_ticks, track->elst_initial_empty_edit_ticks);
if (source->init() != OK) {
delete source;
return NULL;
@@ -4758,7 +4838,8 @@
const Trex *trex,
off64_t firstMoofOffset,
const sp<ItemTable> &itemTable,
- uint64_t elstShiftStartTicks)
+ uint64_t elstShiftStartTicks,
+ uint64_t elstInitialEmptyEditTicks)
: mFormat(format),
mDataSource(dataSource),
mTimescale(timeScale),
@@ -4788,7 +4869,8 @@
mSrcBuffer(NULL),
mIsHeif(itemTable != NULL),
mItemTable(itemTable),
- mElstShiftStartTicks(elstShiftStartTicks) {
+ mElstShiftStartTicks(elstShiftStartTicks),
+ mElstInitialEmptyEditTicks(elstInitialEmptyEditTicks) {
memset(&mTrackFragmentHeaderInfo, 0, sizeof(mTrackFragmentHeaderInfo));
@@ -4907,35 +4989,14 @@
}
CHECK(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_TRACK_ID, &mTrackId));
-
}
status_t MPEG4Source::init() {
- status_t err = OK;
- const char *mime;
- CHECK(AMediaFormat_getString(mFormat, AMEDIAFORMAT_KEY_MIME, &mime));
if (mFirstMoofOffset != 0) {
off64_t offset = mFirstMoofOffset;
- err = parseChunk(&offset);
- if(err == OK && !strncasecmp("video/", mime, 6)
- && !mCurrentSamples.isEmpty()) {
- // Start offset should be less or equal to composition time of first sample.
- // ISO : sample_composition_time_offset, version 0 (unsigned) for major brands.
- mElstShiftStartTicks = std::min(mElstShiftStartTicks,
- (uint64_t)(*mCurrentSamples.begin()).compositionOffset);
- }
- return err;
+ return parseChunk(&offset);
}
-
- if (!strncasecmp("video/", mime, 6)) {
- uint64_t firstSampleCTS = 0;
- err = mSampleTable->getMetaDataForSample(0, NULL, NULL, &firstSampleCTS);
- // Start offset should be less or equal to composition time of first sample.
- // Composition time stamp of first sample cannot be negative.
- mElstShiftStartTicks = std::min(mElstShiftStartTicks, firstSampleCTS);
- }
-
- return err;
+ return OK;
}
MPEG4Source::~MPEG4Source() {
@@ -5783,6 +5844,7 @@
int64_t seekTimeUs;
ReadOptions::SeekMode mode;
+
if (options && options->getSeekTo(&seekTimeUs, &mode)) {
if (mIsHeif) {
@@ -5824,6 +5886,8 @@
}
if( mode != ReadOptions::SEEK_FRAME_INDEX) {
seekTimeUs += ((long double)mElstShiftStartTicks * 1000000) / mTimescale;
+ ALOGV("shifted seekTimeUs :%" PRId64 ", mElstShiftStartTicks:%" PRIu64, seekTimeUs,
+ mElstShiftStartTicks);
}
uint32_t sampleIndex;
@@ -5898,7 +5962,8 @@
off64_t offset = 0;
size_t size = 0;
- uint64_t cts, stts;
+ int64_t cts;
+ uint64_t stts;
bool isSyncSample;
bool newBuffer = false;
if (mBuffer == NULL) {
@@ -5906,14 +5971,15 @@
status_t err;
if (!mIsHeif) {
- err = mSampleTable->getMetaDataForSample(
- mCurrentSampleIndex, &offset, &size, &cts, &isSyncSample, &stts);
+ err = mSampleTable->getMetaDataForSample(mCurrentSampleIndex, &offset, &size,
+ (uint64_t*)&cts, &isSyncSample, &stts);
if(err == OK) {
- /* Composition Time Stamp cannot be negative. Some files have video Sample
- * Time(STTS)delta with zero value(b/117402420). Hence subtract only
- * min(cts, mElstShiftStartTicks), so that audio tracks can be played.
- */
- cts -= std::min(cts, mElstShiftStartTicks);
+ if (mElstInitialEmptyEditTicks > 0) {
+ cts += mElstInitialEmptyEditTicks;
+ } else {
+ // cts can be negative. for example, initial audio samples for gapless playback.
+ cts -= (int64_t)mElstShiftStartTicks;
+ }
}
} else {
@@ -6254,7 +6320,7 @@
off64_t offset = 0;
size_t size = 0;
- uint64_t cts = 0;
+ int64_t cts = 0;
bool isSyncSample = false;
bool newBuffer = false;
if (mBuffer == NULL || mCurrentSampleIndex >= mCurrentSamples.size()) {
@@ -6286,11 +6352,13 @@
offset = smpl->offset;
size = smpl->size;
cts = mCurrentTime + smpl->compositionOffset;
- /* Composition Time Stamp cannot be negative. Some files have video Sample
- * Time(STTS)delta with zero value(b/117402420). Hence subtract only
- * min(cts, mElstShiftStartTicks), so that audio tracks can be played.
- */
- cts -= std::min(cts, mElstShiftStartTicks);
+
+ if (mElstInitialEmptyEditTicks > 0) {
+ cts += mElstInitialEmptyEditTicks;
+ } else {
+ // cts can be negative. for example, initial audio samples for gapless playback.
+ cts -= (int64_t)mElstShiftStartTicks;
+ }
mCurrentTime += smpl->duration;
isSyncSample = (mCurrentSampleIndex == 0);
@@ -6611,6 +6679,12 @@
// The smallest valid chunk is 16 bytes long in this case.
return false;
}
+ if (chunkSize > INT64_MAX) {
+ // reject overly large chunk sizes that could
+ // be interpreted as negative
+ ALOGE("chunk size too large");
+ return false;
+ }
} else if (chunkSize < 8) {
// The smallest valid chunk is 8 bytes long.
@@ -6666,7 +6740,10 @@
case FOURCC("moov"):
{
- moovAtomEndOffset = offset + chunkSize;
+ if (__builtin_add_overflow(offset, chunkSize, &moovAtomEndOffset)) {
+ ALOGE("chunk size + offset would overflow");
+ return false;
+ }
done = true;
break;
@@ -6676,7 +6753,10 @@
break;
}
- offset += chunkSize;
+ if (__builtin_add_overflow(offset, chunkSize, &offset)) {
+ ALOGE("chunk size + offset would overflow");
+ return false;
+ }
}
if (!foundGoodFileType) {
diff --git a/media/extractors/mp4/MPEG4Extractor.h b/media/extractors/mp4/MPEG4Extractor.h
index fcddbb8..53ec6bc 100644
--- a/media/extractors/mp4/MPEG4Extractor.h
+++ b/media/extractors/mp4/MPEG4Extractor.h
@@ -82,14 +82,16 @@
sp<SampleTable> sampleTable;
bool includes_expensive_metadata;
bool skipTrack;
- bool has_elst;
+ bool elst_needs_processing;
/* signed int, ISO Spec allows media_time = -1 for other use cases.
* but we don't support empty edits for now.
*/
int64_t elst_media_time;
uint64_t elst_segment_duration;
- // unsigned int, shift start offset only when media_time > 0.
- uint64_t elstShiftStartTicks;
+ // Shift start offset only when media_time > 0.
+ uint64_t elst_shift_start_ticks;
+ // Initial start offset, empty edit list entry.
+ uint64_t elst_initial_empty_edit_ticks;
bool subsample_encryption;
uint8_t *mTx3gBuffer;
@@ -102,9 +104,11 @@
timescale = 0;
includes_expensive_metadata = false;
skipTrack = false;
- has_elst = false;
+ elst_needs_processing = false;
elst_media_time = 0;
- elstShiftStartTicks = 0;
+ elst_segment_duration = 0;
+ elst_shift_start_ticks = 0;
+ elst_initial_empty_edit_ticks = 0;
subsample_encryption = false;
mTx3gBuffer = NULL;
mTx3gSize = mTx3gFilled = 0;
diff --git a/media/extractors/mpeg2/Android.bp b/media/extractors/mpeg2/Android.bp
index 8638cdc..ef8431e 100644
--- a/media/extractors/mpeg2/Android.bp
+++ b/media/extractors/mpeg2/Android.bp
@@ -1,4 +1,7 @@
-cc_library_shared {
+cc_library {
+ name: "libmpeg2extractor",
+
+ defaults: ["extractor-defaults"],
srcs: [
"ExtractorBundle.cpp",
@@ -6,15 +9,8 @@
"MPEG2TSExtractor.cpp",
],
- include_dirs: [
- "frameworks/av/media/libstagefright",
- "frameworks/av/media/libstagefright/include",
- ],
-
shared_libs: [
"libcgrouprc#29",
- "liblog#10000",
- "libmediandk#29",
"libvndksupport#29",
],
@@ -46,29 +42,15 @@
"libutils",
],
- name: "libmpeg2extractor",
- relative_install_path: "extractors",
-
- compile_multilib: "first",
-
- cflags: [
- "-Werror",
- "-Wall",
- "-fvisibility=hidden",
- ],
- version_script: "exports.lds",
-
- sanitize: {
- // STOPSHIP: turn on cfi once b/139945549 is resolved.
- cfi: false,
- misc_undefined: [
- "unsigned-integer-overflow",
- "signed-integer-overflow",
- ],
- },
-
apex_available: [
"com.android.media",
"test_com.android.media",
],
+
+ static: {
+ apex_available: [
+ // Needed for unit tests
+ "//apex_available:platform",
+ ],
+ },
}
diff --git a/media/extractors/mpeg2/MPEG2PSExtractor.cpp b/media/extractors/mpeg2/MPEG2PSExtractor.cpp
index 002a855..d431b05 100644
--- a/media/extractors/mpeg2/MPEG2PSExtractor.cpp
+++ b/media/extractors/mpeg2/MPEG2PSExtractor.cpp
@@ -20,8 +20,8 @@
#include "MPEG2PSExtractor.h"
-#include "mpeg2ts/AnotherPacketSource.h"
-#include "mpeg2ts/ESQueue.h"
+#include <AnotherPacketSource.h>
+#include <ESQueue.h>
#include <media/stagefright/foundation/ABitReader.h>
#include <media/stagefright/foundation/ABuffer.h>
diff --git a/media/extractors/mpeg2/MPEG2TSExtractor.cpp b/media/extractors/mpeg2/MPEG2TSExtractor.cpp
index 50ce657..9e093eb 100644
--- a/media/extractors/mpeg2/MPEG2TSExtractor.cpp
+++ b/media/extractors/mpeg2/MPEG2TSExtractor.cpp
@@ -24,21 +24,20 @@
#include "MPEG2TSExtractor.h"
-#include <media/DataSourceBase.h>
#include <media/IStreamSource.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/ALooper.h>
#include <media/stagefright/foundation/AUtils.h>
#include <media/stagefright/foundation/MediaKeys.h>
+#include <media/stagefright/DataSourceBase.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/Utils.h>
#include <utils/String8.h>
-#include "mpeg2ts/AnotherPacketSource.h"
-#include "mpeg2ts/ATSParser.h"
+#include <AnotherPacketSource.h>
#include <hidl/HybridInterface.h>
#include <android/hardware/cas/1.0/ICas.h>
diff --git a/media/extractors/mpeg2/MPEG2TSExtractor.h b/media/extractors/mpeg2/MPEG2TSExtractor.h
index dcd1e7b..fd77b08 100644
--- a/media/extractors/mpeg2/MPEG2TSExtractor.h
+++ b/media/extractors/mpeg2/MPEG2TSExtractor.h
@@ -27,7 +27,7 @@
#include <utils/KeyedVector.h>
#include <utils/Vector.h>
-#include "mpeg2ts/ATSParser.h"
+#include <ATSParser.h>
namespace android {
diff --git a/media/extractors/ogg/Android.bp b/media/extractors/ogg/Android.bp
index 604ec59..7aed683 100644
--- a/media/extractors/ogg/Android.bp
+++ b/media/extractors/ogg/Android.bp
@@ -1,9 +1,11 @@
-cc_library_shared {
+cc_library {
+ name: "liboggextractor",
+
+ defaults: ["extractor-defaults"],
srcs: ["OggExtractor.cpp"],
include_dirs: [
- "frameworks/av/media/libstagefright/include",
"external/tremolo",
],
@@ -11,11 +13,6 @@
"libaudio_system_headers",
],
- shared_libs: [
- "liblog",
- "libmediandk",
- ],
-
static_libs: [
"libstagefright_foundation",
"libstagefright_metadatautils",
@@ -23,24 +20,4 @@
"libvorbisidec",
],
- name: "liboggextractor",
- relative_install_path: "extractors",
-
- compile_multilib: "first",
-
- cflags: [
- "-Werror",
- "-Wall",
- "-fvisibility=hidden",
- ],
- version_script: "exports.lds",
-
- sanitize: {
- cfi: true,
- misc_undefined: [
- "unsigned-integer-overflow",
- "signed-integer-overflow",
- ],
- },
-
}
diff --git a/media/extractors/ogg/OggExtractor.cpp b/media/extractors/ogg/OggExtractor.cpp
index bfb2deb..828bcd6 100644
--- a/media/extractors/ogg/OggExtractor.cpp
+++ b/media/extractors/ogg/OggExtractor.cpp
@@ -22,7 +22,7 @@
#include <cutils/properties.h>
#include <utils/Vector.h>
-#include <media/DataSourceBase.h>
+#include <media/stagefright/DataSourceBase.h>
#include <media/ExtractorUtils.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
diff --git a/media/extractors/tests/Android.bp b/media/extractors/tests/Android.bp
new file mode 100644
index 0000000..059c308
--- /dev/null
+++ b/media/extractors/tests/Android.bp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+cc_test {
+ name: "ExtractorUnitTest",
+ gtest: true,
+
+ srcs: ["ExtractorUnitTest.cpp"],
+
+ static_libs: [
+ "libaacextractor",
+ "libamrextractor",
+ "libmp3extractor",
+ "libwavextractor",
+ "liboggextractor",
+ "libflacextractor",
+ "libmidiextractor",
+ "libmkvextractor",
+ "libmpeg2extractor",
+ "libmp4extractor",
+ "libaudioutils",
+ "libdatasource",
+
+ "libstagefright",
+ "libstagefright_id3",
+ "libstagefright_flacdec",
+ "libstagefright_esds",
+ "libstagefright_mpeg2support",
+ "libstagefright_mpeg2extractor",
+ "libstagefright_foundation",
+ "libstagefright_metadatautils",
+
+ "libmedia_midiiowrapper",
+ "libsonivox",
+ "libvorbisidec",
+ "libwebm",
+ "libFLAC",
+ ],
+
+ shared_libs: [
+ "android.hardware.cas@1.0",
+ "android.hardware.cas.native@1.0",
+ "android.hidl.token@1.0-utils",
+ "android.hidl.allocator@1.0",
+ "libbinder",
+ "libbinder_ndk",
+ "libutils",
+ "liblog",
+ "libcutils",
+ "libmediandk",
+ "libmedia",
+ "libcrypto",
+ "libhidlmemory",
+ "libhidlbase",
+ ],
+
+ include_dirs: [
+ "frameworks/av/media/extractors/",
+ "frameworks/av/media/libstagefright/",
+ ],
+
+ compile_multilib: "first",
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ ldflags: [
+ "-Wl",
+ "-Bsymbolic",
+ // to ignore duplicate symbol: GETEXTRACTORDEF
+ "-z muldefs",
+ ],
+
+ sanitize: {
+ cfi: true,
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ },
+}
diff --git a/media/extractors/tests/ExtractorUnitTest.cpp b/media/extractors/tests/ExtractorUnitTest.cpp
new file mode 100644
index 0000000..518166e
--- /dev/null
+++ b/media/extractors/tests/ExtractorUnitTest.cpp
@@ -0,0 +1,528 @@
+/*
+ * Copyright (C) 2019 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 "ExtractorUnitTest"
+#include <utils/Log.h>
+
+#include <datasource/FileSource.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaDataUtils.h>
+
+#include "aac/AACExtractor.h"
+#include "amr/AMRExtractor.h"
+#include "flac/FLACExtractor.h"
+#include "midi/MidiExtractor.h"
+#include "mkv/MatroskaExtractor.h"
+#include "mp3/MP3Extractor.h"
+#include "mp4/MPEG4Extractor.h"
+#include "mp4/SampleTable.h"
+#include "mpeg2/MPEG2PSExtractor.h"
+#include "mpeg2/MPEG2TSExtractor.h"
+#include "ogg/OggExtractor.h"
+#include "wav/WAVExtractor.h"
+
+#include "ExtractorUnitTestEnvironment.h"
+
+using namespace android;
+
+#define OUTPUT_DUMP_FILE "/data/local/tmp/extractorOutput"
+
+constexpr int32_t kMaxCount = 10;
+constexpr int32_t kOpusSeekPreRollUs = 80000; // 80 ms;
+
+static ExtractorUnitTestEnvironment *gEnv = nullptr;
+
+class ExtractorUnitTest : public ::testing::TestWithParam<pair<string, string>> {
+ public:
+ ExtractorUnitTest() : mInputFp(nullptr), mDataSource(nullptr), mExtractor(nullptr) {}
+
+ ~ExtractorUnitTest() {
+ if (mInputFp) {
+ fclose(mInputFp);
+ mInputFp = nullptr;
+ }
+ if (mDataSource) {
+ mDataSource.clear();
+ mDataSource = nullptr;
+ }
+ if (mExtractor) {
+ delete mExtractor;
+ mExtractor = nullptr;
+ }
+ }
+
+ virtual void SetUp() override {
+ mExtractorName = unknown_comp;
+ mDisableTest = false;
+
+ static const std::map<std::string, standardExtractors> mapExtractor = {
+ {"aac", AAC}, {"amr", AMR}, {"mp3", MP3}, {"ogg", OGG},
+ {"wav", WAV}, {"mkv", MKV}, {"flac", FLAC}, {"midi", MIDI},
+ {"mpeg4", MPEG4}, {"mpeg2ts", MPEG2TS}, {"mpeg2ps", MPEG2PS}};
+ // Find the component type
+ string writerFormat = GetParam().first;
+ if (mapExtractor.find(writerFormat) != mapExtractor.end()) {
+ mExtractorName = mapExtractor.at(writerFormat);
+ }
+ if (mExtractorName == standardExtractors::unknown_comp) {
+ cout << "[ WARN ] Test Skipped. Invalid extractor\n";
+ mDisableTest = true;
+ }
+ }
+
+ int32_t setDataSource(string inputFileName);
+
+ int32_t createExtractor();
+
+ enum standardExtractors {
+ AAC,
+ AMR,
+ FLAC,
+ MIDI,
+ MKV,
+ MP3,
+ MPEG4,
+ MPEG2PS,
+ MPEG2TS,
+ OGG,
+ WAV,
+ unknown_comp,
+ };
+
+ bool mDisableTest;
+ standardExtractors mExtractorName;
+
+ FILE *mInputFp;
+ sp<DataSource> mDataSource;
+ MediaExtractorPluginHelper *mExtractor;
+};
+
+int32_t ExtractorUnitTest::setDataSource(string inputFileName) {
+ mInputFp = fopen(inputFileName.c_str(), "rb");
+ if (!mInputFp) {
+ ALOGE("Unable to open input file for reading");
+ return -1;
+ }
+ struct stat buf;
+ stat(inputFileName.c_str(), &buf);
+ int32_t fd = fileno(mInputFp);
+ mDataSource = new FileSource(dup(fd), 0, buf.st_size);
+ if (!mDataSource) return -1;
+ return 0;
+}
+
+int32_t ExtractorUnitTest::createExtractor() {
+ switch (mExtractorName) {
+ case AAC:
+ mExtractor = new AACExtractor(new DataSourceHelper(mDataSource->wrap()), 0);
+ break;
+ case AMR:
+ mExtractor = new AMRExtractor(new DataSourceHelper(mDataSource->wrap()));
+ break;
+ case MP3:
+ mExtractor = new MP3Extractor(new DataSourceHelper(mDataSource->wrap()), nullptr);
+ break;
+ case OGG:
+ mExtractor = new OggExtractor(new DataSourceHelper(mDataSource->wrap()));
+ break;
+ case WAV:
+ mExtractor = new WAVExtractor(new DataSourceHelper(mDataSource->wrap()));
+ break;
+ case MKV:
+ mExtractor = new MatroskaExtractor(new DataSourceHelper(mDataSource->wrap()));
+ break;
+ case FLAC:
+ mExtractor = new FLACExtractor(new DataSourceHelper(mDataSource->wrap()));
+ break;
+ case MPEG4:
+ mExtractor = new MPEG4Extractor(new DataSourceHelper(mDataSource->wrap()));
+ break;
+ case MPEG2TS:
+ mExtractor = new MPEG2TSExtractor(new DataSourceHelper(mDataSource->wrap()));
+ break;
+ case MPEG2PS:
+ mExtractor = new MPEG2PSExtractor(new DataSourceHelper(mDataSource->wrap()));
+ break;
+ case MIDI:
+ mExtractor = new MidiExtractor(mDataSource->wrap());
+ break;
+ default:
+ return -1;
+ }
+ if (!mExtractor) return -1;
+ return 0;
+}
+
+void getSeekablePoints(vector<int64_t> &seekablePoints, MediaTrackHelper *track) {
+ int32_t status = 0;
+ if (!seekablePoints.empty()) {
+ seekablePoints.clear();
+ }
+ int64_t timeStamp;
+ while (status != AMEDIA_ERROR_END_OF_STREAM) {
+ MediaBufferHelper *buffer = nullptr;
+ status = track->read(&buffer);
+ if (buffer) {
+ AMediaFormat *metaData = buffer->meta_data();
+ int32_t isSync = 0;
+ AMediaFormat_getInt32(metaData, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, &isSync);
+ if (isSync) {
+ AMediaFormat_getInt64(metaData, AMEDIAFORMAT_KEY_TIME_US, &timeStamp);
+ seekablePoints.push_back(timeStamp);
+ }
+ buffer->release();
+ }
+ }
+}
+
+TEST_P(ExtractorUnitTest, CreateExtractorTest) {
+ if (mDisableTest) return;
+
+ ALOGV("Checks if a valid extractor is created for a given input file");
+ string inputFileName = gEnv->getRes() + GetParam().second;
+
+ ASSERT_EQ(setDataSource(inputFileName), 0)
+ << "SetDataSource failed for" << GetParam().first << "extractor";
+
+ ASSERT_EQ(createExtractor(), 0)
+ << "Extractor creation failed for" << GetParam().first << "extractor";
+
+ // A valid extractor instace should return success for following calls
+ ASSERT_GT(mExtractor->countTracks(), 0);
+
+ AMediaFormat *format = AMediaFormat_new();
+ ASSERT_NE(format, nullptr) << "AMediaFormat_new returned null AMediaformat";
+
+ ASSERT_EQ(mExtractor->getMetaData(format), AMEDIA_OK);
+ AMediaFormat_delete(format);
+}
+
+TEST_P(ExtractorUnitTest, ExtractorTest) {
+ if (mDisableTest) return;
+
+ ALOGV("Validates %s Extractor for a given input file", GetParam().first.c_str());
+ string inputFileName = gEnv->getRes() + GetParam().second;
+
+ int32_t status = setDataSource(inputFileName);
+ ASSERT_EQ(status, 0) << "SetDataSource failed for" << GetParam().first << "extractor";
+
+ status = createExtractor();
+ ASSERT_EQ(status, 0) << "Extractor creation failed for" << GetParam().first << "extractor";
+
+ int32_t numTracks = mExtractor->countTracks();
+ ASSERT_GT(numTracks, 0) << "Extractor didn't find any track for the given clip";
+
+ for (int32_t idx = 0; idx < numTracks; idx++) {
+ MediaTrackHelper *track = mExtractor->getTrack(idx);
+ ASSERT_NE(track, nullptr) << "Failed to get track for index " << idx;
+
+ CMediaTrack *cTrack = wrap(track);
+ ASSERT_NE(cTrack, nullptr) << "Failed to get track wrapper for index " << idx;
+
+ MediaBufferGroup *bufferGroup = new MediaBufferGroup();
+ status = cTrack->start(track, bufferGroup->wrap());
+ ASSERT_EQ(OK, (media_status_t)status) << "Failed to start the track";
+
+ FILE *outFp = fopen((OUTPUT_DUMP_FILE + to_string(idx)).c_str(), "wb");
+ if (!outFp) {
+ ALOGW("Unable to open output file for dumping extracted stream");
+ }
+
+ while (status != AMEDIA_ERROR_END_OF_STREAM) {
+ MediaBufferHelper *buffer = nullptr;
+ status = track->read(&buffer);
+ ALOGV("track->read Status = %d buffer %p", status, buffer);
+ if (buffer) {
+ ALOGV("buffer->data %p buffer->size() %zu buffer->range_length() %zu",
+ buffer->data(), buffer->size(), buffer->range_length());
+ if (outFp) fwrite(buffer->data(), 1, buffer->range_length(), outFp);
+ buffer->release();
+ }
+ }
+ if (outFp) fclose(outFp);
+ status = cTrack->stop(track);
+ ASSERT_EQ(OK, status) << "Failed to stop the track";
+ delete bufferGroup;
+ delete track;
+ }
+}
+
+TEST_P(ExtractorUnitTest, MetaDataComparisonTest) {
+ if (mDisableTest) return;
+
+ ALOGV("Validates Extractor's meta data for a given input file");
+ string inputFileName = gEnv->getRes() + GetParam().second;
+
+ int32_t status = setDataSource(inputFileName);
+ ASSERT_EQ(status, 0) << "SetDataSource failed for" << GetParam().first << "extractor";
+
+ status = createExtractor();
+ ASSERT_EQ(status, 0) << "Extractor creation failed for" << GetParam().first << "extractor";
+
+ int32_t numTracks = mExtractor->countTracks();
+ ASSERT_GT(numTracks, 0) << "Extractor didn't find any track for the given clip";
+
+ AMediaFormat *extractorFormat = AMediaFormat_new();
+ ASSERT_NE(extractorFormat, nullptr) << "AMediaFormat_new returned null AMediaformat";
+ AMediaFormat *trackFormat = AMediaFormat_new();
+ ASSERT_NE(trackFormat, nullptr) << "AMediaFormat_new returned null AMediaformat";
+
+ for (int32_t idx = 0; idx < numTracks; idx++) {
+ MediaTrackHelper *track = mExtractor->getTrack(idx);
+ ASSERT_NE(track, nullptr) << "Failed to get track for index " << idx;
+
+ CMediaTrack *cTrack = wrap(track);
+ ASSERT_NE(cTrack, nullptr) << "Failed to get track wrapper for index " << idx;
+
+ MediaBufferGroup *bufferGroup = new MediaBufferGroup();
+ status = cTrack->start(track, bufferGroup->wrap());
+ ASSERT_EQ(OK, (media_status_t)status) << "Failed to start the track";
+
+ status = mExtractor->getTrackMetaData(extractorFormat, idx, 1);
+ ASSERT_EQ(OK, (media_status_t)status) << "Failed to get trackMetaData";
+
+ status = track->getFormat(trackFormat);
+ ASSERT_EQ(OK, (media_status_t)status) << "Failed to get track meta data";
+
+ const char *extractorMime, *trackMime;
+ AMediaFormat_getString(extractorFormat, AMEDIAFORMAT_KEY_MIME, &extractorMime);
+ AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &trackMime);
+ ASSERT_TRUE(!strcmp(extractorMime, trackMime))
+ << "Extractor's format doesn't match track format";
+
+ if (!strncmp(extractorMime, "audio/", 6)) {
+ int32_t exSampleRate, exChannelCount;
+ int32_t trackSampleRate, trackChannelCount;
+ ASSERT_TRUE(AMediaFormat_getInt32(extractorFormat, AMEDIAFORMAT_KEY_CHANNEL_COUNT,
+ &exChannelCount));
+ ASSERT_TRUE(AMediaFormat_getInt32(extractorFormat, AMEDIAFORMAT_KEY_SAMPLE_RATE,
+ &exSampleRate));
+ ASSERT_TRUE(AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_CHANNEL_COUNT,
+ &trackChannelCount));
+ ASSERT_TRUE(AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_SAMPLE_RATE,
+ &trackSampleRate));
+ ASSERT_EQ(exChannelCount, trackChannelCount) << "ChannelCount not as expected";
+ ASSERT_EQ(exSampleRate, trackSampleRate) << "SampleRate not as expected";
+ } else {
+ int32_t exWidth, exHeight;
+ int32_t trackWidth, trackHeight;
+ ASSERT_TRUE(AMediaFormat_getInt32(extractorFormat, AMEDIAFORMAT_KEY_WIDTH, &exWidth));
+ ASSERT_TRUE(AMediaFormat_getInt32(extractorFormat, AMEDIAFORMAT_KEY_HEIGHT, &exHeight));
+ ASSERT_TRUE(AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_WIDTH, &trackWidth));
+ ASSERT_TRUE(AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_HEIGHT, &trackHeight));
+ ASSERT_EQ(exWidth, trackWidth) << "Width not as expected";
+ ASSERT_EQ(exHeight, trackHeight) << "Height not as expected";
+ }
+ status = cTrack->stop(track);
+ ASSERT_EQ(OK, status) << "Failed to stop the track";
+ delete bufferGroup;
+ delete track;
+ }
+ AMediaFormat_delete(trackFormat);
+ AMediaFormat_delete(extractorFormat);
+}
+
+TEST_P(ExtractorUnitTest, MultipleStartStopTest) {
+ if (mDisableTest) return;
+
+ ALOGV("Test %s extractor for multiple start and stop calls", GetParam().first.c_str());
+ string inputFileName = gEnv->getRes() + GetParam().second;
+
+ int32_t status = setDataSource(inputFileName);
+ ASSERT_EQ(status, 0) << "SetDataSource failed for" << GetParam().first << "extractor";
+
+ status = createExtractor();
+ ASSERT_EQ(status, 0) << "Extractor creation failed for" << GetParam().first << "extractor";
+
+ int32_t numTracks = mExtractor->countTracks();
+ ASSERT_GT(numTracks, 0) << "Extractor didn't find any track for the given clip";
+
+ // start/stop the tracks multiple times
+ for (int32_t count = 0; count < kMaxCount; count++) {
+ for (int32_t idx = 0; idx < numTracks; idx++) {
+ MediaTrackHelper *track = mExtractor->getTrack(idx);
+ ASSERT_NE(track, nullptr) << "Failed to get track for index " << idx;
+
+ CMediaTrack *cTrack = wrap(track);
+ ASSERT_NE(cTrack, nullptr) << "Failed to get track wrapper for index " << idx;
+
+ MediaBufferGroup *bufferGroup = new MediaBufferGroup();
+ status = cTrack->start(track, bufferGroup->wrap());
+ ASSERT_EQ(OK, (media_status_t)status) << "Failed to start the track";
+ MediaBufferHelper *buffer = nullptr;
+ status = track->read(&buffer);
+ if (buffer) {
+ ALOGV("buffer->data %p buffer->size() %zu buffer->range_length() %zu",
+ buffer->data(), buffer->size(), buffer->range_length());
+ buffer->release();
+ }
+ status = cTrack->stop(track);
+ ASSERT_EQ(OK, status) << "Failed to stop the track";
+ delete bufferGroup;
+ delete track;
+ }
+ }
+}
+
+TEST_P(ExtractorUnitTest, SeekTest) {
+ // Both Flac and Wav extractor can give samples from any pts and mark the given sample as
+ // sync frame. So, this seek test is not applicable to FLAC and WAV extractors
+ if (mDisableTest || mExtractorName == FLAC || mExtractorName == WAV) return;
+
+ ALOGV("Validates %s Extractor behaviour for different seek modes", GetParam().first.c_str());
+ string inputFileName = gEnv->getRes() + GetParam().second;
+
+ int32_t status = setDataSource(inputFileName);
+ ASSERT_EQ(status, 0) << "SetDataSource failed for" << GetParam().first << "extractor";
+
+ status = createExtractor();
+ ASSERT_EQ(status, 0) << "Extractor creation failed for" << GetParam().first << "extractor";
+
+ int32_t numTracks = mExtractor->countTracks();
+ ASSERT_GT(numTracks, 0) << "Extractor didn't find any track for the given clip";
+
+ uint32_t seekFlag = mExtractor->flags();
+ if (!(seekFlag & MediaExtractorPluginHelper::CAN_SEEK)) {
+ cout << "[ WARN ] Test Skipped. " << GetParam().first
+ << " Extractor doesn't support seek\n";
+ return;
+ }
+
+ vector<int64_t> seekablePoints;
+ for (int32_t idx = 0; idx < numTracks; idx++) {
+ MediaTrackHelper *track = mExtractor->getTrack(idx);
+ ASSERT_NE(track, nullptr) << "Failed to get track for index " << idx;
+
+ CMediaTrack *cTrack = wrap(track);
+ ASSERT_NE(cTrack, nullptr) << "Failed to get track wrapper for index " << idx;
+
+ // Get all the seekable points of a given input
+ MediaBufferGroup *bufferGroup = new MediaBufferGroup();
+ status = cTrack->start(track, bufferGroup->wrap());
+ ASSERT_EQ(OK, (media_status_t)status) << "Failed to start the track";
+ getSeekablePoints(seekablePoints, track);
+ ASSERT_GT(seekablePoints.size(), 0)
+ << "Failed to get seekable points for " << GetParam().first << " extractor";
+
+ AMediaFormat *trackFormat = AMediaFormat_new();
+ ASSERT_NE(trackFormat, nullptr) << "AMediaFormat_new returned null format";
+ status = track->getFormat(trackFormat);
+ ASSERT_EQ(OK, (media_status_t)status) << "Failed to get track meta data";
+
+ bool isOpus = false;
+ const char *mime;
+ AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &mime);
+ if (!strcmp(mime, "audio/opus")) isOpus = true;
+ AMediaFormat_delete(trackFormat);
+
+ int32_t seekIdx = 0;
+ size_t seekablePointsSize = seekablePoints.size();
+ for (int32_t mode = CMediaTrackReadOptions::SEEK_PREVIOUS_SYNC;
+ mode <= CMediaTrackReadOptions::SEEK_CLOSEST; mode++) {
+ for (int32_t seekCount = 0; seekCount < kMaxCount; seekCount++) {
+ seekIdx = rand() % seekablePointsSize + 1;
+ if (seekIdx >= seekablePointsSize) seekIdx = seekablePointsSize - 1;
+
+ int64_t seekToTimeStamp = seekablePoints[seekIdx];
+ if (seekablePointsSize > 1) {
+ int64_t prevTimeStamp = seekablePoints[seekIdx - 1];
+ seekToTimeStamp = seekToTimeStamp - ((seekToTimeStamp - prevTimeStamp) >> 3);
+ }
+
+ // Opus has a seekPreRollUs. TimeStamp returned by the
+ // extractor is calculated based on (seekPts - seekPreRollUs).
+ // So we add the preRoll value to the timeStamp we want to seek to.
+ if (isOpus) {
+ seekToTimeStamp += kOpusSeekPreRollUs;
+ }
+
+ MediaTrackHelper::ReadOptions *options = new MediaTrackHelper::ReadOptions(
+ mode | CMediaTrackReadOptions::SEEK, seekToTimeStamp);
+ ASSERT_NE(options, nullptr) << "Cannot create read option";
+
+ MediaBufferHelper *buffer = nullptr;
+ status = track->read(&buffer, options);
+ if (status == AMEDIA_ERROR_END_OF_STREAM) {
+ delete options;
+ continue;
+ }
+ if (buffer) {
+ AMediaFormat *metaData = buffer->meta_data();
+ int64_t timeStamp;
+ AMediaFormat_getInt64(metaData, AMEDIAFORMAT_KEY_TIME_US, &timeStamp);
+ buffer->release();
+
+ // CMediaTrackReadOptions::SEEK is 8. Using mask 0111b to get true modes
+ switch (mode & 0x7) {
+ case CMediaTrackReadOptions::SEEK_PREVIOUS_SYNC:
+ if (seekablePointsSize == 1) {
+ EXPECT_EQ(timeStamp, seekablePoints[seekIdx]);
+ } else {
+ EXPECT_EQ(timeStamp, seekablePoints[seekIdx - 1]);
+ }
+ break;
+ case CMediaTrackReadOptions::SEEK_NEXT_SYNC:
+ case CMediaTrackReadOptions::SEEK_CLOSEST_SYNC:
+ case CMediaTrackReadOptions::SEEK_CLOSEST:
+ EXPECT_EQ(timeStamp, seekablePoints[seekIdx]);
+ break;
+ default:
+ break;
+ }
+ }
+ delete options;
+ }
+ }
+ status = cTrack->stop(track);
+ ASSERT_EQ(OK, status) << "Failed to stop the track";
+ delete bufferGroup;
+ delete track;
+ }
+ seekablePoints.clear();
+}
+
+// TODO: (b/145332185)
+// Add MIDI inputs
+INSTANTIATE_TEST_SUITE_P(ExtractorUnitTestAll, ExtractorUnitTest,
+ ::testing::Values(make_pair("aac", "loudsoftaac.aac"),
+ make_pair("amr", "testamr.amr"),
+ make_pair("amr", "amrwb.wav"),
+ make_pair("ogg", "john_cage.ogg"),
+ make_pair("wav", "monotestgsm.wav"),
+ make_pair("mpeg2ts", "segment000001.ts"),
+ make_pair("flac", "sinesweepflac.flac"),
+ make_pair("ogg", "testopus.opus"),
+ make_pair("mkv", "sinesweepvorbis.mkv"),
+ make_pair("mpeg4", "sinesweepoggmp4.mp4"),
+ make_pair("mp3", "sinesweepmp3lame.mp3"),
+ make_pair("mkv", "swirl_144x136_vp9.webm"),
+ make_pair("mkv", "swirl_144x136_vp8.webm"),
+ make_pair("mpeg2ps", "swirl_144x136_mpeg2.mpg"),
+ make_pair("mpeg4", "swirl_132x130_mpeg4.mp4")));
+
+int main(int argc, char **argv) {
+ gEnv = new ExtractorUnitTestEnvironment();
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = gEnv->initFromOptions(argc, argv);
+ if (status == 0) {
+ status = RUN_ALL_TESTS();
+ ALOGV("Test result = %d\n", status);
+ }
+ return status;
+}
diff --git a/media/extractors/tests/ExtractorUnitTestEnvironment.h b/media/extractors/tests/ExtractorUnitTestEnvironment.h
new file mode 100644
index 0000000..fce8fc2
--- /dev/null
+++ b/media/extractors/tests/ExtractorUnitTestEnvironment.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __EXTRACTOR_UNIT_TEST_ENVIRONMENT_H__
+#define __EXTRACTOR_UNIT_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class ExtractorUnitTestEnvironment : public ::testing::Environment {
+ public:
+ ExtractorUnitTestEnvironment() : res("/data/local/tmp/") {}
+
+ // Parses the command line arguments
+ int initFromOptions(int argc, char **argv);
+
+ void setRes(const char *_res) { res = _res; }
+
+ const string getRes() const { return res; }
+
+ private:
+ string res;
+};
+
+int ExtractorUnitTestEnvironment::initFromOptions(int argc, char **argv) {
+ static struct option options[] = {{"res", required_argument, 0, 'P'}, {0, 0, 0, 0}};
+
+ while (true) {
+ int index = 0;
+ int c = getopt_long(argc, argv, "P:", options, &index);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'P':
+ setRes(optarg);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr,
+ "unrecognized option: %s\n\n"
+ "usage: %s <gtest options> <test options>\n\n"
+ "test options are:\n\n"
+ "-P, --path: Resource files directory location\n",
+ argv[optind ?: 1], argv[0]);
+ return 2;
+ }
+ return 0;
+}
+
+#endif // __EXTRACTOR_UNIT_TEST_ENVIRONMENT_H__
diff --git a/media/extractors/tests/README.md b/media/extractors/tests/README.md
new file mode 100644
index 0000000..6e02d3e
--- /dev/null
+++ b/media/extractors/tests/README.md
@@ -0,0 +1,34 @@
+## Media Testing ##
+---
+#### Extractor :
+The Extractor Test Suite validates the extractors available in the device.
+
+Run the following steps to build the test suite:
+```
+m ExtractorUnitTest
+```
+
+The 32-bit binaries will be created in the following path : ${OUT}/data/nativetest/
+
+The 64-bit binaries will be created in the following path : ${OUT}/data/nativetest64/
+
+To test 64-bit binary push binaries from nativetest64.
+```
+adb push ${OUT}/data/nativetest64/ExtractorUnitTest/ExtractorUnitTest /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/ExtractorUnitTest/ExtractorUnitTest /data/local/tmp/
+```
+
+The resource file for the tests is taken from [here](https://drive.google.com/drive/folders/1Z9nCIRB6pGLvb5mPkF8BURa5Nc6cY9pY). Push these files into device for testing.
+Download extractor folder and push all the files in this folder to /data/local/tmp/ on the device.
+```
+adb push extractor /data/local/tmp/
+```
+
+usage: ExtractorUnitTest -P \<path_to_folder\>
+```
+adb shell /data/local/tmp/ExtractorUnitTest -P /data/local/tmp/extractor/
+```
diff --git a/media/extractors/wav/Android.bp b/media/extractors/wav/Android.bp
index 7e89271..8ce5c3f 100644
--- a/media/extractors/wav/Android.bp
+++ b/media/extractors/wav/Android.bp
@@ -1,4 +1,7 @@
-cc_library_shared {
+cc_library {
+ name: "libwavextractor",
+
+ defaults: ["extractor-defaults"],
srcs: ["WAVExtractor.cpp"],
@@ -8,8 +11,6 @@
shared_libs: [
"libbinder_ndk",
- "liblog",
- "libmediandk",
],
static_libs: [
@@ -17,25 +18,4 @@
"libfifo",
"libstagefright_foundation",
],
-
- name: "libwavextractor",
- relative_install_path: "extractors",
-
- compile_multilib: "first",
-
- cflags: [
- "-Werror",
- "-Wall",
- "-fvisibility=hidden",
- ],
- version_script: "exports.lds",
-
- sanitize: {
- cfi: true,
- misc_undefined: [
- "unsigned-integer-overflow",
- "signed-integer-overflow",
- ],
- },
-
}
diff --git a/media/libaaudio/examples/input_monitor/Android.bp b/media/libaaudio/examples/input_monitor/Android.bp
index 5d399b5..d8c5843 100644
--- a/media/libaaudio/examples/input_monitor/Android.bp
+++ b/media/libaaudio/examples/input_monitor/Android.bp
@@ -5,7 +5,6 @@
cflags: ["-Wall", "-Werror"],
shared_libs: ["libaaudio"],
header_libs: ["libaaudio_example_utils"],
- pack_relocations: false,
}
cc_test {
@@ -15,5 +14,4 @@
cflags: ["-Wall", "-Werror"],
shared_libs: ["libaaudio"],
header_libs: ["libaaudio_example_utils"],
- pack_relocations: false,
}
diff --git a/media/libaaudio/examples/loopback/Android.bp b/media/libaaudio/examples/loopback/Android.bp
index 53e5020..5b7d956 100644
--- a/media/libaaudio/examples/loopback/Android.bp
+++ b/media/libaaudio/examples/loopback/Android.bp
@@ -9,5 +9,4 @@
"libaudioutils",
],
header_libs: ["libaaudio_example_utils"],
- pack_relocations: false,
}
diff --git a/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h b/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h
deleted file mode 100644
index 8eb70b1..0000000
--- a/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h
+++ /dev/null
@@ -1,1114 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-/**
- * Tools for measuring latency and for detecting glitches.
- * These classes are pure math and can be used with any audio system.
- */
-
-#ifndef AAUDIO_EXAMPLES_LOOPBACK_ANALYSER_H
-#define AAUDIO_EXAMPLES_LOOPBACK_ANALYSER_H
-
-#include <algorithm>
-#include <assert.h>
-#include <cctype>
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include <audio_utils/sndfile.h>
-
-// Tag for machine readable results as property = value pairs
-#define LOOPBACK_RESULT_TAG "RESULT: "
-
-constexpr int32_t kDefaultSampleRate = 48000;
-constexpr int32_t kMillisPerSecond = 1000;
-constexpr int32_t kMinLatencyMillis = 4; // arbitrary and very low
-constexpr int32_t kMaxLatencyMillis = 400; // arbitrary and generous
-constexpr double kMaxEchoGain = 10.0; // based on experiments, otherwise too noisy
-constexpr double kMinimumConfidence = 0.5;
-
-static void printAudioScope(float sample) {
- const int maxStars = 80; // arbitrary, fits on one line
- char c = '*';
- if (sample < -1.0) {
- sample = -1.0;
- c = '$';
- } else if (sample > 1.0) {
- sample = 1.0;
- c = '$';
- }
- int numSpaces = (int) (((sample + 1.0) * 0.5) * maxStars);
- for (int i = 0; i < numSpaces; i++) {
- putchar(' ');
- }
- printf("%c\n", c);
-}
-
-/*
-
-FIR filter designed with
-http://t-filter.appspot.com
-
-sampling frequency: 48000 Hz
-
-* 0 Hz - 8000 Hz
- gain = 1.2
- desired ripple = 5 dB
- actual ripple = 5.595266169703693 dB
-
-* 12000 Hz - 20000 Hz
- gain = 0
- desired attenuation = -40 dB
- actual attenuation = -37.58691566571914 dB
-
-*/
-
-#define FILTER_TAP_NUM 11
-
-static const float sFilterTaps8000[FILTER_TAP_NUM] = {
- -0.05944219353343189f,
- -0.07303434839503208f,
- -0.037690487672689066f,
- 0.1870480506596512f,
- 0.3910337357836833f,
- 0.5333672385425637f,
- 0.3910337357836833f,
- 0.1870480506596512f,
- -0.037690487672689066f,
- -0.07303434839503208f,
- -0.05944219353343189f
-};
-
-class LowPassFilter {
-public:
-
- /*
- * Filter one input sample.
- * @return filtered output
- */
- float filter(float input) {
- float output = 0.0f;
- mX[mCursor] = input;
- // Index backwards over x.
- int xIndex = mCursor + FILTER_TAP_NUM;
- // Write twice so we avoid having to wrap in the middle of the convolution.
- mX[xIndex] = input;
- for (int i = 0; i < FILTER_TAP_NUM; i++) {
- output += sFilterTaps8000[i] * mX[xIndex--];
- }
- if (++mCursor >= FILTER_TAP_NUM) {
- mCursor = 0;
- }
- return output;
- }
-
- /**
- * @return true if PASSED
- */
- bool test() {
- // Measure the impulse of the filter at different phases so we exercise
- // all the wraparound cases in the FIR.
- for (int offset = 0; offset < (FILTER_TAP_NUM * 2); offset++ ) {
- // printf("LowPassFilter: cursor = %d\n", mCursor);
- // Offset by one each time.
- if (filter(0.0f) != 0.0f) {
- printf("ERROR: filter should return 0.0 before impulse response\n");
- return false;
- }
- for (int i = 0; i < FILTER_TAP_NUM; i++) {
- float output = filter((i == 0) ? 1.0f : 0.0f); // impulse
- if (output != sFilterTaps8000[i]) {
- printf("ERROR: filter should return impulse response\n");
- return false;
- }
- }
- for (int i = 0; i < FILTER_TAP_NUM; i++) {
- if (filter(0.0f) != 0.0f) {
- printf("ERROR: filter should return 0.0 after impulse response\n");
- return false;
- }
- }
- }
- return true;
- }
-
-private:
- float mX[FILTER_TAP_NUM * 2]{}; // twice as big as needed to avoid wrapping
- int32_t mCursor = 0;
-};
-
-// A narrow impulse seems to have better immunity against over estimating the
-// latency due to detecting subharmonics by the auto-correlator.
-static const float s_Impulse[] = {
- 0.0f, 0.0f, 0.0f, 0.0f, 0.3f, // silence on each side of the impulse
- 0.99f, 0.0f, -0.99f, // bipolar with one zero crossing in middle
- -0.3f, 0.0f, 0.0f, 0.0f, 0.0f
-};
-
-constexpr int32_t kImpulseSizeInFrames = (int32_t)(sizeof(s_Impulse) / sizeof(s_Impulse[0]));
-
-class PseudoRandom {
-public:
- PseudoRandom() {}
- PseudoRandom(int64_t seed)
- : mSeed(seed)
- {}
-
- /**
- * Returns the next random double from -1.0 to 1.0
- *
- * @return value from -1.0 to 1.0
- */
- double nextRandomDouble() {
- return nextRandomInteger() * (0.5 / (((int32_t)1) << 30));
- }
-
- /** Calculate random 32 bit number using linear-congruential method. */
- int32_t nextRandomInteger() {
- // Use values for 64-bit sequence from MMIX by Donald Knuth.
- mSeed = (mSeed * (int64_t)6364136223846793005) + (int64_t)1442695040888963407;
- return (int32_t) (mSeed >> 32); // The higher bits have a longer sequence.
- }
-
-private:
- int64_t mSeed = 99887766;
-};
-
-
-typedef struct LatencyReport_s {
- double latencyInFrames;
- double confidence;
-} LatencyReport;
-
-static double calculateCorrelation(const float *a,
- const float *b,
- int windowSize)
-{
- double correlation = 0.0;
- double sumProducts = 0.0;
- double sumSquares = 0.0;
-
- // Correlate a against b.
- for (int i = 0; i < windowSize; i++) {
- float s1 = a[i];
- float s2 = b[i];
- // Use a normalized cross-correlation.
- sumProducts += s1 * s2;
- sumSquares += ((s1 * s1) + (s2 * s2));
- }
-
- if (sumSquares >= 0.00000001) {
- correlation = (float) (2.0 * sumProducts / sumSquares);
- }
- return correlation;
-}
-
-static int measureLatencyFromEchos(const float *data,
- int32_t numFloats,
- int32_t sampleRate,
- LatencyReport *report) {
- // Allocate results array
- const int minReasonableLatencyFrames = sampleRate * kMinLatencyMillis / kMillisPerSecond;
- const int maxReasonableLatencyFrames = sampleRate * kMaxLatencyMillis / kMillisPerSecond;
- int32_t maxCorrelationSize = maxReasonableLatencyFrames * 3;
- int numCorrelations = std::min(numFloats, maxCorrelationSize);
- float *correlations = new float[numCorrelations]{};
- float *harmonicSums = new float[numCorrelations]{};
-
- // Perform sliding auto-correlation.
- // Skip first frames to avoid huge peak at zero offset.
- for (int i = minReasonableLatencyFrames; i < numCorrelations; i++) {
- int32_t remaining = numFloats - i;
- float correlation = (float) calculateCorrelation(&data[i], data, remaining);
- correlations[i] = correlation;
- // printf("correlation[%d] = %f\n", ic, correlation);
- }
-
- // Apply a technique similar to Harmonic Product Spectrum Analysis to find echo fundamental.
- // Add higher harmonics mapped onto lower harmonics. This reinforces the "fundamental" echo.
- const int numEchoes = 8;
- for (int partial = 1; partial < numEchoes; partial++) {
- for (int i = minReasonableLatencyFrames; i < numCorrelations; i++) {
- harmonicSums[i / partial] += correlations[i] / partial;
- }
- }
-
- // Find highest peak in correlation array.
- float maxCorrelation = 0.0;
- int peakIndex = 0;
- for (int i = 0; i < numCorrelations; i++) {
- if (harmonicSums[i] > maxCorrelation) {
- maxCorrelation = harmonicSums[i];
- peakIndex = i;
- // printf("maxCorrelation = %f at %d\n", maxCorrelation, peakIndex);
- }
- }
- report->latencyInFrames = peakIndex;
-/*
- {
- int32_t topPeak = peakIndex * 7 / 2;
- for (int i = 0; i < topPeak; i++) {
- float sample = harmonicSums[i];
- printf("%4d: %7.5f ", i, sample);
- printAudioScope(sample);
- }
- }
-*/
-
- // Calculate confidence.
- if (maxCorrelation < 0.001) {
- report->confidence = 0.0;
- } else {
- // Compare peak to average value around peak.
- int32_t numSamples = std::min(numCorrelations, peakIndex * 2);
- if (numSamples <= 0) {
- report->confidence = 0.0;
- } else {
- double sum = 0.0;
- for (int i = 0; i < numSamples; i++) {
- sum += harmonicSums[i];
- }
- const double average = sum / numSamples;
- const double ratio = average / maxCorrelation; // will be < 1.0
- report->confidence = 1.0 - sqrt(ratio);
- }
- }
-
- delete[] correlations;
- delete[] harmonicSums;
- return 0;
-}
-
-class AudioRecording
-{
-public:
- AudioRecording() {
- }
- ~AudioRecording() {
- delete[] mData;
- }
-
- void allocate(int maxFrames) {
- delete[] mData;
- mData = new float[maxFrames];
- mMaxFrames = maxFrames;
- }
-
- // Write SHORT data from the first channel.
- int32_t write(int16_t *inputData, int32_t inputChannelCount, int32_t numFrames) {
- // stop at end of buffer
- if ((mFrameCounter + numFrames) > mMaxFrames) {
- numFrames = mMaxFrames - mFrameCounter;
- }
- for (int i = 0; i < numFrames; i++) {
- mData[mFrameCounter++] = inputData[i * inputChannelCount] * (1.0f / 32768);
- }
- return numFrames;
- }
-
- // Write FLOAT data from the first channel.
- int32_t write(float *inputData, int32_t inputChannelCount, int32_t numFrames) {
- // stop at end of buffer
- if ((mFrameCounter + numFrames) > mMaxFrames) {
- numFrames = mMaxFrames - mFrameCounter;
- }
- for (int i = 0; i < numFrames; i++) {
- mData[mFrameCounter++] = inputData[i * inputChannelCount];
- }
- return numFrames;
- }
-
- int32_t size() {
- return mFrameCounter;
- }
-
- float *getData() {
- return mData;
- }
-
- void setSampleRate(int32_t sampleRate) {
- mSampleRate = sampleRate;
- }
-
- int32_t getSampleRate() {
- return mSampleRate;
- }
-
- int save(const char *fileName, bool writeShorts = true) {
- SNDFILE *sndFile = nullptr;
- int written = 0;
- SF_INFO info = {
- .frames = mFrameCounter,
- .samplerate = mSampleRate,
- .channels = 1,
- .format = SF_FORMAT_WAV | (writeShorts ? SF_FORMAT_PCM_16 : SF_FORMAT_FLOAT)
- };
-
- sndFile = sf_open(fileName, SFM_WRITE, &info);
- if (sndFile == nullptr) {
- printf("AudioRecording::save(%s) failed to open file\n", fileName);
- return -errno;
- }
-
- written = sf_writef_float(sndFile, mData, mFrameCounter);
-
- sf_close(sndFile);
- return written;
- }
-
- int load(const char *fileName) {
- SNDFILE *sndFile = nullptr;
- SF_INFO info;
-
- sndFile = sf_open(fileName, SFM_READ, &info);
- if (sndFile == nullptr) {
- printf("AudioRecording::load(%s) failed to open file\n", fileName);
- return -errno;
- }
-
- assert(info.channels == 1);
- assert(info.format == SF_FORMAT_FLOAT);
-
- setSampleRate(info.samplerate);
- allocate(info.frames);
- mFrameCounter = sf_readf_float(sndFile, mData, info.frames);
-
- sf_close(sndFile);
- return mFrameCounter;
- }
-
- /**
- * Square the samples so they are all positive and so the peaks are emphasized.
- */
- void square() {
- for (int i = 0; i < mFrameCounter; i++) {
- const float sample = mData[i];
- mData[i] = sample * sample;
- }
- }
-
- /**
- * Low pass filter the recording using a simple FIR filter.
- * Note that the lowpass filter cutoff tracks the sample rate.
- * That is OK because the impulse width is a fixed number of samples.
- */
- void lowPassFilter() {
- for (int i = 0; i < mFrameCounter; i++) {
- mData[i] = mLowPassFilter.filter(mData[i]);
- }
- }
-
- /**
- * Remove DC offset using a one-pole one-zero IIR filter.
- */
- void dcBlocker() {
- const float R = 0.996; // narrow notch at zero Hz
- float x1 = 0.0;
- float y1 = 0.0;
- for (int i = 0; i < mFrameCounter; i++) {
- const float x = mData[i];
- const float y = x - x1 + (R * y1);
- mData[i] = y;
- y1 = y;
- x1 = x;
- }
- }
-
-private:
- float *mData = nullptr;
- int32_t mFrameCounter = 0;
- int32_t mMaxFrames = 0;
- int32_t mSampleRate = kDefaultSampleRate; // common default
- LowPassFilter mLowPassFilter;
-};
-
-// ====================================================================================
-class LoopbackProcessor {
-public:
- virtual ~LoopbackProcessor() = default;
-
-
- enum process_result {
- PROCESS_RESULT_OK,
- PROCESS_RESULT_GLITCH
- };
-
- virtual void reset() {}
-
- virtual process_result process(float *inputData, int inputChannelCount,
- float *outputData, int outputChannelCount,
- int numFrames) = 0;
-
-
- virtual void report() = 0;
-
- virtual void printStatus() {};
-
- int32_t getResult() {
- return mResult;
- }
-
- void setResult(int32_t result) {
- mResult = result;
- }
-
- virtual bool isDone() {
- return false;
- }
-
- virtual int save(const char *fileName) {
- (void) fileName;
- return AAUDIO_ERROR_UNIMPLEMENTED;
- }
-
- virtual int load(const char *fileName) {
- (void) fileName;
- return AAUDIO_ERROR_UNIMPLEMENTED;
- }
-
- virtual void setSampleRate(int32_t sampleRate) {
- mSampleRate = sampleRate;
- }
-
- int32_t getSampleRate() {
- return mSampleRate;
- }
-
- // Measure peak amplitude of buffer.
- static float measurePeakAmplitude(float *inputData, int inputChannelCount, int numFrames) {
- float peak = 0.0f;
- for (int i = 0; i < numFrames; i++) {
- const float pos = fabs(*inputData);
- if (pos > peak) {
- peak = pos;
- }
- inputData += inputChannelCount;
- }
- return peak;
- }
-
-
-private:
- int32_t mSampleRate = kDefaultSampleRate;
- int32_t mResult = 0;
-};
-
-class PeakDetector {
-public:
- float process(float input) {
- float output = mPrevious * mDecay;
- if (input > output) {
- output = input;
- }
- mPrevious = output;
- return output;
- }
-
-private:
- float mDecay = 0.99f;
- float mPrevious = 0.0f;
-};
-
-// ====================================================================================
-/**
- * Measure latency given a loopback stream data.
- * Uses a state machine to cycle through various stages including:
- *
- */
-class EchoAnalyzer : public LoopbackProcessor {
-public:
-
- EchoAnalyzer() : LoopbackProcessor() {
- mAudioRecording.allocate(2 * getSampleRate());
- mAudioRecording.setSampleRate(getSampleRate());
- }
-
- void setSampleRate(int32_t sampleRate) override {
- LoopbackProcessor::setSampleRate(sampleRate);
- mAudioRecording.setSampleRate(sampleRate);
- }
-
- void reset() override {
- mDownCounter = getSampleRate() / 2;
- mLoopCounter = 0;
- mMeasuredLoopGain = 0.0f;
- mEchoGain = 1.0f;
- mState = STATE_INITIAL_SILENCE;
- }
-
- virtual bool isDone() {
- return mState == STATE_DONE || mState == STATE_FAILED;
- }
-
- void setGain(float gain) {
- mEchoGain = gain;
- }
-
- float getGain() {
- return mEchoGain;
- }
-
- bool testLowPassFilter() {
- LowPassFilter filter;
- return filter.test();
- }
-
- void report() override {
- printf("EchoAnalyzer ---------------\n");
- if (getResult() != 0) {
- printf(LOOPBACK_RESULT_TAG "result = %d\n", getResult());
- return;
- }
-
- // printf("LowPassFilter test %s\n", testLowPassFilter() ? "PASSED" : "FAILED");
-
- printf(LOOPBACK_RESULT_TAG "measured.gain = %8f\n", mMeasuredLoopGain);
- printf(LOOPBACK_RESULT_TAG "echo.gain = %8f\n", mEchoGain);
- printf(LOOPBACK_RESULT_TAG "test.state = %8d\n", mState);
- printf(LOOPBACK_RESULT_TAG "test.state.name = %8s\n", convertStateToText(mState));
-
- if (mState == STATE_WAITING_FOR_SILENCE) {
- printf("WARNING - Stuck waiting for silence. Input may be too noisy!\n");
- setResult(ERROR_NOISY);
- } else if (mMeasuredLoopGain >= 0.9999) {
- printf(" ERROR - clipping, turn down volume slightly\n");
- setResult(ERROR_CLIPPING);
- } else if (mState != STATE_DONE && mState != STATE_GATHERING_ECHOS) {
- printf("WARNING - Bad state. Check volume on device.\n");
- setResult(ERROR_INVALID_STATE);
- } else {
- // Cleanup the signal to improve the auto-correlation.
- mAudioRecording.dcBlocker();
- mAudioRecording.square();
- mAudioRecording.lowPassFilter();
-
- printf("Please wait several seconds for auto-correlation to complete.\n");
- measureLatencyFromEchos(mAudioRecording.getData(),
- mAudioRecording.size(),
- getSampleRate(),
- &mLatencyReport);
-
- double latencyMillis = kMillisPerSecond * (double) mLatencyReport.latencyInFrames
- / getSampleRate();
- printf(LOOPBACK_RESULT_TAG "latency.frames = %8.2f\n",
- mLatencyReport.latencyInFrames);
- printf(LOOPBACK_RESULT_TAG "latency.msec = %8.2f\n",
- latencyMillis);
- printf(LOOPBACK_RESULT_TAG "latency.confidence = %8.6f\n",
- mLatencyReport.confidence);
- if (mLatencyReport.confidence < kMinimumConfidence) {
- printf(" ERROR - confidence too low!\n");
- setResult(ERROR_CONFIDENCE);
- }
- }
- }
-
- void printStatus() override {
- printf("st = %d, echo gain = %f ", mState, mEchoGain);
- }
-
- void sendImpulses(float *outputData, int outputChannelCount, int numFrames) {
- while (numFrames-- > 0) {
- float sample = s_Impulse[mSampleIndex++];
- if (mSampleIndex >= kImpulseSizeInFrames) {
- mSampleIndex = 0;
- }
-
- *outputData = sample;
- outputData += outputChannelCount;
- }
- }
-
- void sendOneImpulse(float *outputData, int outputChannelCount) {
- mSampleIndex = 0;
- sendImpulses(outputData, outputChannelCount, kImpulseSizeInFrames);
- }
-
- // @return number of frames for a typical block of processing
- int32_t getBlockFrames() {
- return getSampleRate() / 8;
- }
-
- process_result process(float *inputData, int inputChannelCount,
- float *outputData, int outputChannelCount,
- int numFrames) override {
- int channelsValid = std::min(inputChannelCount, outputChannelCount);
- float peak = 0.0f;
- int numWritten;
- int numSamples;
-
- echo_state nextState = mState;
-
- switch (mState) {
- case STATE_INITIAL_SILENCE:
- // Output silence at the beginning.
- numSamples = numFrames * outputChannelCount;
- for (int i = 0; i < numSamples; i++) {
- outputData[i] = 0;
- }
- mDownCounter -= numFrames;
- if (mDownCounter <= 0) {
- nextState = STATE_MEASURING_GAIN;
- //printf("%5d: switch to STATE_MEASURING_GAIN\n", mLoopCounter);
- mDownCounter = getBlockFrames() * 2;
- }
- break;
-
- case STATE_MEASURING_GAIN:
- sendImpulses(outputData, outputChannelCount, numFrames);
- peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames);
- // If we get several in a row then go to next state.
- if (peak > mPulseThreshold) {
- mDownCounter -= numFrames;
- if (mDownCounter <= 0) {
- //printf("%5d: switch to STATE_WAITING_FOR_SILENCE, measured peak = %f\n",
- // mLoopCounter, peak);
- mDownCounter = getBlockFrames();
- mMeasuredLoopGain = peak; // assumes original pulse amplitude is one
- mSilenceThreshold = peak * 0.1; // scale silence to measured pulse
- // Calculate gain that will give us a nice decaying echo.
- mEchoGain = mDesiredEchoGain / mMeasuredLoopGain;
- if (mEchoGain > kMaxEchoGain) {
- printf("ERROR - loop gain too low. Increase the volume.\n");
- nextState = STATE_FAILED;
- } else {
- nextState = STATE_WAITING_FOR_SILENCE;
- }
- }
- } else if (numFrames > kImpulseSizeInFrames){ // ignore short callbacks
- mDownCounter = getBlockFrames();
- }
- break;
-
- case STATE_WAITING_FOR_SILENCE:
- // Output silence and wait for the echos to die down.
- numSamples = numFrames * outputChannelCount;
- for (int i = 0; i < numSamples; i++) {
- outputData[i] = 0;
- }
- peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames);
- // If we get several in a row then go to next state.
- if (peak < mSilenceThreshold) {
- mDownCounter -= numFrames;
- if (mDownCounter <= 0) {
- nextState = STATE_SENDING_PULSE;
- //printf("%5d: switch to STATE_SENDING_PULSE\n", mLoopCounter);
- mDownCounter = getBlockFrames();
- }
- } else {
- mDownCounter = getBlockFrames();
- }
- break;
-
- case STATE_SENDING_PULSE:
- mAudioRecording.write(inputData, inputChannelCount, numFrames);
- sendOneImpulse(outputData, outputChannelCount);
- nextState = STATE_GATHERING_ECHOS;
- //printf("%5d: switch to STATE_GATHERING_ECHOS\n", mLoopCounter);
- break;
-
- case STATE_GATHERING_ECHOS:
- numWritten = mAudioRecording.write(inputData, inputChannelCount, numFrames);
- peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames);
- if (peak > mMeasuredLoopGain) {
- mMeasuredLoopGain = peak; // AGC might be raising gain so adjust it on the fly.
- // Recalculate gain that will give us a nice decaying echo.
- mEchoGain = mDesiredEchoGain / mMeasuredLoopGain;
- }
- // Echo input to output.
- for (int i = 0; i < numFrames; i++) {
- int ic;
- for (ic = 0; ic < channelsValid; ic++) {
- outputData[ic] = inputData[ic] * mEchoGain;
- }
- for (; ic < outputChannelCount; ic++) {
- outputData[ic] = 0;
- }
- inputData += inputChannelCount;
- outputData += outputChannelCount;
- }
- if (numWritten < numFrames) {
- nextState = STATE_DONE;
- }
- break;
-
- case STATE_DONE:
- case STATE_FAILED:
- default:
- break;
- }
-
- mState = nextState;
- mLoopCounter++;
- return PROCESS_RESULT_OK;
- }
-
- int save(const char *fileName) override {
- return mAudioRecording.save(fileName);
- }
-
- int load(const char *fileName) override {
- int result = mAudioRecording.load(fileName);
- setSampleRate(mAudioRecording.getSampleRate());
- mState = STATE_DONE;
- return result;
- }
-
-private:
-
- enum error_code {
- ERROR_OK = 0,
- ERROR_NOISY = -99,
- ERROR_CLIPPING,
- ERROR_CONFIDENCE,
- ERROR_INVALID_STATE
- };
-
- enum echo_state {
- STATE_INITIAL_SILENCE,
- STATE_MEASURING_GAIN,
- STATE_WAITING_FOR_SILENCE,
- STATE_SENDING_PULSE,
- STATE_GATHERING_ECHOS,
- STATE_DONE,
- STATE_FAILED
- };
-
- const char *convertStateToText(echo_state state) {
- const char *result = "Unknown";
- switch(state) {
- case STATE_INITIAL_SILENCE:
- result = "INIT";
- break;
- case STATE_MEASURING_GAIN:
- result = "GAIN";
- break;
- case STATE_WAITING_FOR_SILENCE:
- result = "SILENCE";
- break;
- case STATE_SENDING_PULSE:
- result = "PULSE";
- break;
- case STATE_GATHERING_ECHOS:
- result = "ECHOS";
- break;
- case STATE_DONE:
- result = "DONE";
- break;
- case STATE_FAILED:
- result = "FAILED";
- break;
- }
- return result;
- }
-
-
- int32_t mDownCounter = 500;
- int32_t mLoopCounter = 0;
- int32_t mSampleIndex = 0;
- float mPulseThreshold = 0.02f;
- float mSilenceThreshold = 0.002f;
- float mMeasuredLoopGain = 0.0f;
- float mDesiredEchoGain = 0.95f;
- float mEchoGain = 1.0f;
- echo_state mState = STATE_INITIAL_SILENCE;
-
- AudioRecording mAudioRecording; // contains only the input after the gain detection burst
- LatencyReport mLatencyReport;
- // PeakDetector mPeakDetector;
-};
-
-
-// ====================================================================================
-/**
- * Output a steady sinewave and analyze the return signal.
- *
- * Use a cosine transform to measure the predicted magnitude and relative phase of the
- * looped back sine wave. Then generate a predicted signal and compare with the actual signal.
- */
-class SineAnalyzer : public LoopbackProcessor {
-public:
-
- void report() override {
- printf("SineAnalyzer ------------------\n");
- printf(LOOPBACK_RESULT_TAG "peak.amplitude = %8f\n", mPeakAmplitude);
- printf(LOOPBACK_RESULT_TAG "sine.magnitude = %8f\n", mMagnitude);
- printf(LOOPBACK_RESULT_TAG "peak.noise = %8f\n", mPeakNoise);
- printf(LOOPBACK_RESULT_TAG "rms.noise = %8f\n", mRootMeanSquareNoise);
- float amplitudeRatio = mMagnitude / mPeakNoise;
- float signalToNoise = amplitudeRatio * amplitudeRatio;
- printf(LOOPBACK_RESULT_TAG "signal.to.noise = %8.2f\n", signalToNoise);
- float signalToNoiseDB = 10.0 * log(signalToNoise);
- printf(LOOPBACK_RESULT_TAG "signal.to.noise.db = %8.2f\n", signalToNoiseDB);
- if (signalToNoiseDB < MIN_SNRATIO_DB) {
- printf("ERROR - signal to noise ratio is too low! < %d dB. Adjust volume.\n", MIN_SNRATIO_DB);
- setResult(ERROR_NOISY);
- }
- printf(LOOPBACK_RESULT_TAG "frames.accumulated = %8d\n", mFramesAccumulated);
- printf(LOOPBACK_RESULT_TAG "sine.period = %8d\n", mSinePeriod);
- printf(LOOPBACK_RESULT_TAG "test.state = %8d\n", mState);
- printf(LOOPBACK_RESULT_TAG "frame.count = %8d\n", mFrameCounter);
- // Did we ever get a lock?
- bool gotLock = (mState == STATE_LOCKED) || (mGlitchCount > 0);
- if (!gotLock) {
- printf("ERROR - failed to lock on reference sine tone\n");
- setResult(ERROR_NO_LOCK);
- } else {
- // Only print if meaningful.
- printf(LOOPBACK_RESULT_TAG "glitch.count = %8d\n", mGlitchCount);
- printf(LOOPBACK_RESULT_TAG "max.glitch = %8f\n", mMaxGlitchDelta);
- if (mGlitchCount > 0) {
- printf("ERROR - number of glitches > 0\n");
- setResult(ERROR_GLITCHES);
- }
- }
- }
-
- void printStatus() override {
- printf("st = %d, #gl = %3d,", mState, mGlitchCount);
- }
-
- double calculateMagnitude(double *phasePtr = NULL) {
- if (mFramesAccumulated == 0) {
- return 0.0;
- }
- double sinMean = mSinAccumulator / mFramesAccumulated;
- double cosMean = mCosAccumulator / mFramesAccumulated;
- double magnitude = 2.0 * sqrt( (sinMean * sinMean) + (cosMean * cosMean ));
- if( phasePtr != NULL )
- {
- double phase = M_PI_2 - atan2( sinMean, cosMean );
- *phasePtr = phase;
- }
- return magnitude;
- }
-
- /**
- * @param inputData contains microphone data with sine signal feedback
- * @param outputData contains the reference sine wave
- */
- process_result process(float *inputData, int inputChannelCount,
- float *outputData, int outputChannelCount,
- int numFrames) override {
- process_result result = PROCESS_RESULT_OK;
- mProcessCount++;
-
- float peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames);
- if (peak > mPeakAmplitude) {
- mPeakAmplitude = peak;
- }
-
- for (int i = 0; i < numFrames; i++) {
- bool sineEnabled = true;
- float sample = inputData[i * inputChannelCount];
-
- float sinOut = sinf(mPhase);
-
- switch (mState) {
- case STATE_IDLE:
- sineEnabled = false;
- mDownCounter--;
- if (mDownCounter <= 0) {
- mState = STATE_MEASURE_NOISE;
- mDownCounter = NOISE_FRAME_COUNT;
- }
- break;
- case STATE_MEASURE_NOISE:
- sineEnabled = false;
- mPeakNoise = std::max(abs(sample), mPeakNoise);
- mNoiseSumSquared += sample * sample;
- mDownCounter--;
- if (mDownCounter <= 0) {
- mState = STATE_WAITING_FOR_SIGNAL;
- mRootMeanSquareNoise = sqrt(mNoiseSumSquared / NOISE_FRAME_COUNT);
- mTolerance = std::max(MIN_TOLERANCE, mPeakNoise * 2.0f);
- mPhase = 0.0; // prevent spike at start
- }
- break;
-
- case STATE_IMMUNE:
- mDownCounter--;
- if (mDownCounter <= 0) {
- mState = STATE_WAITING_FOR_SIGNAL;
- }
- break;
-
- case STATE_WAITING_FOR_SIGNAL:
- if (peak > mThreshold) {
- mState = STATE_WAITING_FOR_LOCK;
- //printf("%5d: switch to STATE_WAITING_FOR_LOCK\n", mFrameCounter);
- resetAccumulator();
- }
- break;
-
- case STATE_WAITING_FOR_LOCK:
- mSinAccumulator += sample * sinOut;
- mCosAccumulator += sample * cosf(mPhase);
- mFramesAccumulated++;
- // Must be a multiple of the period or the calculation will not be accurate.
- if (mFramesAccumulated == mSinePeriod * PERIODS_NEEDED_FOR_LOCK) {
- mPhaseOffset = 0.0;
- mMagnitude = calculateMagnitude(&mPhaseOffset);
- if (mMagnitude > mThreshold) {
- if (fabs(mPreviousPhaseOffset - mPhaseOffset) < 0.001) {
- mState = STATE_LOCKED;
- //printf("%5d: switch to STATE_LOCKED\n", mFrameCounter);
- }
- mPreviousPhaseOffset = mPhaseOffset;
- }
- resetAccumulator();
- }
- break;
-
- case STATE_LOCKED: {
- // Predict next sine value
- float predicted = sinf(mPhase + mPhaseOffset) * mMagnitude;
- // printf(" predicted = %f, actual = %f\n", predicted, sample);
-
- float diff = predicted - sample;
- float absDiff = fabs(diff);
- mMaxGlitchDelta = std::max(mMaxGlitchDelta, absDiff);
- if (absDiff > mTolerance) {
- mGlitchCount++;
- result = PROCESS_RESULT_GLITCH;
- //printf("%5d: Got a glitch # %d, predicted = %f, actual = %f\n",
- // mFrameCounter, mGlitchCount, predicted, sample);
- mState = STATE_IMMUNE;
- mDownCounter = mSinePeriod * PERIODS_IMMUNE;
- }
-
- // Track incoming signal and slowly adjust magnitude to account
- // for drift in the DRC or AGC.
- mSinAccumulator += sample * sinOut;
- mCosAccumulator += sample * cosf(mPhase);
- mFramesAccumulated++;
- // Must be a multiple of the period or the calculation will not be accurate.
- if (mFramesAccumulated == mSinePeriod) {
- const double coefficient = 0.1;
- double phaseOffset = 0.0;
- double magnitude = calculateMagnitude(&phaseOffset);
- // One pole averaging filter.
- mMagnitude = (mMagnitude * (1.0 - coefficient)) + (magnitude * coefficient);
- resetAccumulator();
- }
- } break;
- }
-
- float output = 0.0f;
- // Output sine wave so we can measure it.
- if (sineEnabled) {
- output = (sinOut * mOutputAmplitude)
- + (mWhiteNoise.nextRandomDouble() * mNoiseAmplitude);
- // printf("%5d: sin(%f) = %f, %f\n", i, mPhase, sinOut, mPhaseIncrement);
- // advance and wrap phase
- mPhase += mPhaseIncrement;
- if (mPhase > M_PI) {
- mPhase -= (2.0 * M_PI);
- }
- }
- outputData[i * outputChannelCount] = output;
-
-
- mFrameCounter++;
- }
- return result;
- }
-
- void resetAccumulator() {
- mFramesAccumulated = 0;
- mSinAccumulator = 0.0;
- mCosAccumulator = 0.0;
- }
-
- void reset() override {
- mGlitchCount = 0;
- mState = STATE_IDLE;
- mDownCounter = IDLE_FRAME_COUNT;
- mPhaseIncrement = 2.0 * M_PI / mSinePeriod;
- printf("phaseInc = %f for period %d\n", mPhaseIncrement, mSinePeriod);
- resetAccumulator();
- mProcessCount = 0;
- mPeakNoise = 0.0f;
- mNoiseSumSquared = 0.0;
- mRootMeanSquareNoise = 0.0;
- mPhase = 0.0f;
- mMaxGlitchDelta = 0.0;
- }
-
-private:
-
- enum error_code {
- OK,
- ERROR_NO_LOCK = -80,
- ERROR_GLITCHES,
- ERROR_NOISY
- };
-
- enum sine_state_t {
- STATE_IDLE,
- STATE_MEASURE_NOISE,
- STATE_IMMUNE,
- STATE_WAITING_FOR_SIGNAL,
- STATE_WAITING_FOR_LOCK,
- STATE_LOCKED
- };
-
- enum constants {
- // Arbitrary durations, assuming 48000 Hz
- IDLE_FRAME_COUNT = 48 * 100,
- NOISE_FRAME_COUNT = 48 * 600,
- PERIODS_NEEDED_FOR_LOCK = 8,
- PERIODS_IMMUNE = 2,
- MIN_SNRATIO_DB = 65
- };
-
- static constexpr float MIN_TOLERANCE = 0.01;
-
- int mSinePeriod = 79;
- double mPhaseIncrement = 0.0;
- double mPhase = 0.0;
- double mPhaseOffset = 0.0;
- double mPreviousPhaseOffset = 0.0;
- double mMagnitude = 0.0;
- double mThreshold = 0.005;
- double mTolerance = MIN_TOLERANCE;
- int32_t mFramesAccumulated = 0;
- int32_t mProcessCount = 0;
- double mSinAccumulator = 0.0;
- double mCosAccumulator = 0.0;
- float mMaxGlitchDelta = 0.0f;
- int32_t mGlitchCount = 0;
- double mPeakAmplitude = 0.0;
- int mDownCounter = IDLE_FRAME_COUNT;
- int32_t mFrameCounter = 0;
- float mOutputAmplitude = 0.75;
-
- // measure background noise
- float mPeakNoise = 0.0f;
- double mNoiseSumSquared = 0.0;
- double mRootMeanSquareNoise = 0.0;
-
- PseudoRandom mWhiteNoise;
- float mNoiseAmplitude = 0.00; // Used to experiment with warbling caused by DRC.
-
- sine_state_t mState = STATE_IDLE;
-};
-
-#undef LOOPBACK_RESULT_TAG
-
-#endif /* AAUDIO_EXAMPLES_LOOPBACK_ANALYSER_H */
diff --git a/media/libaaudio/examples/loopback/src/analyzer/GlitchAnalyzer.h b/media/libaaudio/examples/loopback/src/analyzer/GlitchAnalyzer.h
new file mode 100644
index 0000000..04435d1
--- /dev/null
+++ b/media/libaaudio/examples/loopback/src/analyzer/GlitchAnalyzer.h
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANALYZER_GLITCH_ANALYZER_H
+#define ANALYZER_GLITCH_ANALYZER_H
+
+#include <algorithm>
+#include <cctype>
+#include <iomanip>
+#include <iostream>
+
+#include "LatencyAnalyzer.h"
+#include "PseudoRandom.h"
+
+/**
+ * Output a steady sine wave and analyze the return signal.
+ *
+ * Use a cosine transform to measure the predicted magnitude and relative phase of the
+ * looped back sine wave. Then generate a predicted signal and compare with the actual signal.
+ */
+class GlitchAnalyzer : public LoopbackProcessor {
+public:
+
+ int32_t getState() const {
+ return mState;
+ }
+
+ double getPeakAmplitude() const {
+ return mPeakFollower.getLevel();
+ }
+
+ double getTolerance() {
+ return mTolerance;
+ }
+
+ void setTolerance(double tolerance) {
+ mTolerance = tolerance;
+ mScaledTolerance = mMagnitude * mTolerance;
+ }
+
+ void setMagnitude(double magnitude) {
+ mMagnitude = magnitude;
+ mScaledTolerance = mMagnitude * mTolerance;
+ }
+
+ int32_t getGlitchCount() const {
+ return mGlitchCount;
+ }
+
+ int32_t getStateFrameCount(int state) const {
+ return mStateFrameCounters[state];
+ }
+
+ double getSignalToNoiseDB() {
+ static const double threshold = 1.0e-14;
+ if (mMeanSquareSignal < threshold || mMeanSquareNoise < threshold) {
+ return 0.0;
+ } else {
+ double signalToNoise = mMeanSquareSignal / mMeanSquareNoise; // power ratio
+ double signalToNoiseDB = 10.0 * log(signalToNoise);
+ if (signalToNoiseDB < MIN_SNR_DB) {
+ ALOGD("ERROR - signal to noise ratio is too low! < %d dB. Adjust volume.",
+ MIN_SNR_DB);
+ setResult(ERROR_VOLUME_TOO_LOW);
+ }
+ return signalToNoiseDB;
+ }
+ }
+
+ std::string analyze() override {
+ std::stringstream report;
+ report << "GlitchAnalyzer ------------------\n";
+ report << LOOPBACK_RESULT_TAG "peak.amplitude = " << std::setw(8)
+ << getPeakAmplitude() << "\n";
+ report << LOOPBACK_RESULT_TAG "sine.magnitude = " << std::setw(8)
+ << mMagnitude << "\n";
+ report << LOOPBACK_RESULT_TAG "rms.noise = " << std::setw(8)
+ << mMeanSquareNoise << "\n";
+ report << LOOPBACK_RESULT_TAG "signal.to.noise.db = " << std::setw(8)
+ << getSignalToNoiseDB() << "\n";
+ report << LOOPBACK_RESULT_TAG "frames.accumulated = " << std::setw(8)
+ << mFramesAccumulated << "\n";
+ report << LOOPBACK_RESULT_TAG "sine.period = " << std::setw(8)
+ << mSinePeriod << "\n";
+ report << LOOPBACK_RESULT_TAG "test.state = " << std::setw(8)
+ << mState << "\n";
+ report << LOOPBACK_RESULT_TAG "frame.count = " << std::setw(8)
+ << mFrameCounter << "\n";
+ // Did we ever get a lock?
+ bool gotLock = (mState == STATE_LOCKED) || (mGlitchCount > 0);
+ if (!gotLock) {
+ report << "ERROR - failed to lock on reference sine tone.\n";
+ setResult(ERROR_NO_LOCK);
+ } else {
+ // Only print if meaningful.
+ report << LOOPBACK_RESULT_TAG "glitch.count = " << std::setw(8)
+ << mGlitchCount << "\n";
+ report << LOOPBACK_RESULT_TAG "max.glitch = " << std::setw(8)
+ << mMaxGlitchDelta << "\n";
+ if (mGlitchCount > 0) {
+ report << "ERROR - number of glitches > 0\n";
+ setResult(ERROR_GLITCHES);
+ }
+ }
+ return report.str();
+ }
+
+ void printStatus() override {
+ ALOGD("st = %d, #gl = %3d,", mState, mGlitchCount);
+ }
+ /**
+ * Calculate the magnitude of the component of the input signal
+ * that matches the analysis frequency.
+ * Also calculate the phase that we can use to create a
+ * signal that matches that component.
+ * The phase will be between -PI and +PI.
+ */
+ double calculateMagnitude(double *phasePtr = nullptr) {
+ if (mFramesAccumulated == 0) {
+ return 0.0;
+ }
+ double sinMean = mSinAccumulator / mFramesAccumulated;
+ double cosMean = mCosAccumulator / mFramesAccumulated;
+ double magnitude = 2.0 * sqrt((sinMean * sinMean) + (cosMean * cosMean));
+ if (phasePtr != nullptr) {
+ double phase = M_PI_2 - atan2(sinMean, cosMean);
+ *phasePtr = phase;
+ }
+ return magnitude;
+ }
+
+ /**
+ * @param frameData contains microphone data with sine signal feedback
+ * @param channelCount
+ */
+ result_code processInputFrame(float *frameData, int /* channelCount */) override {
+ result_code result = RESULT_OK;
+
+ float sample = frameData[0];
+ float peak = mPeakFollower.process(sample);
+
+ // Force a periodic glitch to test the detector!
+ if (mForceGlitchDuration > 0) {
+ if (mForceGlitchCounter == 0) {
+ ALOGE("%s: force a glitch!!", __func__);
+ mForceGlitchCounter = getSampleRate();
+ } else if (mForceGlitchCounter <= mForceGlitchDuration) {
+ // Force an abrupt offset.
+ sample += (sample > 0.0) ? -0.5f : 0.5f;
+ }
+ --mForceGlitchCounter;
+ }
+
+ mStateFrameCounters[mState]++; // count how many frames we are in each state
+
+ switch (mState) {
+ case STATE_IDLE:
+ mDownCounter--;
+ if (mDownCounter <= 0) {
+ mState = STATE_IMMUNE;
+ mDownCounter = IMMUNE_FRAME_COUNT;
+ mInputPhase = 0.0; // prevent spike at start
+ mOutputPhase = 0.0;
+ }
+ break;
+
+ case STATE_IMMUNE:
+ mDownCounter--;
+ if (mDownCounter <= 0) {
+ mState = STATE_WAITING_FOR_SIGNAL;
+ }
+ break;
+
+ case STATE_WAITING_FOR_SIGNAL:
+ if (peak > mThreshold) {
+ mState = STATE_WAITING_FOR_LOCK;
+ //ALOGD("%5d: switch to STATE_WAITING_FOR_LOCK", mFrameCounter);
+ resetAccumulator();
+ }
+ break;
+
+ case STATE_WAITING_FOR_LOCK:
+ mSinAccumulator += sample * sinf(mInputPhase);
+ mCosAccumulator += sample * cosf(mInputPhase);
+ mFramesAccumulated++;
+ // Must be a multiple of the period or the calculation will not be accurate.
+ if (mFramesAccumulated == mSinePeriod * PERIODS_NEEDED_FOR_LOCK) {
+ double phaseOffset = 0.0;
+ setMagnitude(calculateMagnitude(&phaseOffset));
+// ALOGD("%s() mag = %f, offset = %f, prev = %f",
+// __func__, mMagnitude, mPhaseOffset, mPreviousPhaseOffset);
+ if (mMagnitude > mThreshold) {
+ if (abs(phaseOffset) < kMaxPhaseError) {
+ mState = STATE_LOCKED;
+// ALOGD("%5d: switch to STATE_LOCKED", mFrameCounter);
+ }
+ // Adjust mInputPhase to match measured phase
+ mInputPhase += phaseOffset;
+ }
+ resetAccumulator();
+ }
+ incrementInputPhase();
+ break;
+
+ case STATE_LOCKED: {
+ // Predict next sine value
+ double predicted = sinf(mInputPhase) * mMagnitude;
+ double diff = predicted - sample;
+ double absDiff = fabs(diff);
+ mMaxGlitchDelta = std::max(mMaxGlitchDelta, absDiff);
+ if (absDiff > mScaledTolerance) {
+ result = ERROR_GLITCHES;
+ onGlitchStart();
+// LOGI("diff glitch detected, absDiff = %g", absDiff);
+ } else {
+ mSumSquareSignal += predicted * predicted;
+ mSumSquareNoise += diff * diff;
+ // Track incoming signal and slowly adjust magnitude to account
+ // for drift in the DRC or AGC.
+ mSinAccumulator += sample * sinf(mInputPhase);
+ mCosAccumulator += sample * cosf(mInputPhase);
+ mFramesAccumulated++;
+ // Must be a multiple of the period or the calculation will not be accurate.
+ if (mFramesAccumulated == mSinePeriod) {
+ const double coefficient = 0.1;
+ double phaseOffset = 0.0;
+ double magnitude = calculateMagnitude(&phaseOffset);
+ // One pole averaging filter.
+ setMagnitude((mMagnitude * (1.0 - coefficient)) + (magnitude * coefficient));
+
+ mMeanSquareNoise = mSumSquareNoise * mInverseSinePeriod;
+ mMeanSquareSignal = mSumSquareSignal * mInverseSinePeriod;
+ resetAccumulator();
+
+ if (abs(phaseOffset) > kMaxPhaseError) {
+ result = ERROR_GLITCHES;
+ onGlitchStart();
+ ALOGD("phase glitch detected, phaseOffset = %g", phaseOffset);
+ } else if (mMagnitude < mThreshold) {
+ result = ERROR_GLITCHES;
+ onGlitchStart();
+ ALOGD("magnitude glitch detected, mMagnitude = %g", mMagnitude);
+ }
+ }
+ }
+ incrementInputPhase();
+ } break;
+
+ case STATE_GLITCHING: {
+ // Predict next sine value
+ mGlitchLength++;
+ double predicted = sinf(mInputPhase) * mMagnitude;
+ double diff = predicted - sample;
+ double absDiff = fabs(diff);
+ mMaxGlitchDelta = std::max(mMaxGlitchDelta, absDiff);
+ if (absDiff < mScaledTolerance) { // close enough?
+ // If we get a full sine period of non-glitch samples in a row then consider the glitch over.
+ // We don't want to just consider a zero crossing the end of a glitch.
+ if (mNonGlitchCount++ > mSinePeriod) {
+ onGlitchEnd();
+ }
+ } else {
+ mNonGlitchCount = 0;
+ if (mGlitchLength > (4 * mSinePeriod)) {
+ relock();
+ }
+ }
+ incrementInputPhase();
+ } break;
+
+ case NUM_STATES: // not a real state
+ break;
+ }
+
+ mFrameCounter++;
+
+ return result;
+ }
+
+ // advance and wrap phase
+ void incrementInputPhase() {
+ mInputPhase += mPhaseIncrement;
+ if (mInputPhase > M_PI) {
+ mInputPhase -= (2.0 * M_PI);
+ }
+ }
+
+ // advance and wrap phase
+ void incrementOutputPhase() {
+ mOutputPhase += mPhaseIncrement;
+ if (mOutputPhase > M_PI) {
+ mOutputPhase -= (2.0 * M_PI);
+ }
+ }
+
+ /**
+ * @param frameData upon return, contains the reference sine wave
+ * @param channelCount
+ */
+ result_code processOutputFrame(float *frameData, int channelCount) override {
+ float output = 0.0f;
+ // Output sine wave so we can measure it.
+ if (mState != STATE_IDLE) {
+ float sinOut = sinf(mOutputPhase);
+ incrementOutputPhase();
+ output = (sinOut * mOutputAmplitude)
+ + (mWhiteNoise.nextRandomDouble() * kNoiseAmplitude);
+ // ALOGD("sin(%f) = %f, %f\n", mOutputPhase, sinOut, mPhaseIncrement);
+ }
+ frameData[0] = output;
+ for (int i = 1; i < channelCount; i++) {
+ frameData[i] = 0.0f;
+ }
+ return RESULT_OK;
+ }
+
+ void onGlitchStart() {
+ mGlitchCount++;
+// ALOGD("%5d: STARTED a glitch # %d", mFrameCounter, mGlitchCount);
+ mState = STATE_GLITCHING;
+ mGlitchLength = 1;
+ mNonGlitchCount = 0;
+ }
+
+ void onGlitchEnd() {
+// ALOGD("%5d: ENDED a glitch # %d, length = %d", mFrameCounter, mGlitchCount, mGlitchLength);
+ mState = STATE_LOCKED;
+ resetAccumulator();
+ }
+
+ // reset the sine wave detector
+ void resetAccumulator() {
+ mFramesAccumulated = 0;
+ mSinAccumulator = 0.0;
+ mCosAccumulator = 0.0;
+ mSumSquareSignal = 0.0;
+ mSumSquareNoise = 0.0;
+ }
+
+ void relock() {
+// ALOGD("relock: %d because of a very long %d glitch", mFrameCounter, mGlitchLength);
+ mState = STATE_WAITING_FOR_LOCK;
+ resetAccumulator();
+ }
+
+ void reset() override {
+ LoopbackProcessor::reset();
+ mState = STATE_IDLE;
+ mDownCounter = IDLE_FRAME_COUNT;
+ resetAccumulator();
+ }
+
+ void prepareToTest() override {
+ LoopbackProcessor::prepareToTest();
+ mSinePeriod = getSampleRate() / kTargetGlitchFrequency;
+ mOutputPhase = 0.0f;
+ mInverseSinePeriod = 1.0 / mSinePeriod;
+ mPhaseIncrement = 2.0 * M_PI * mInverseSinePeriod;
+ mGlitchCount = 0;
+ mMaxGlitchDelta = 0.0;
+ for (int i = 0; i < NUM_STATES; i++) {
+ mStateFrameCounters[i] = 0;
+ }
+ }
+
+private:
+
+ // These must match the values in GlitchActivity.java
+ enum sine_state_t {
+ STATE_IDLE, // beginning
+ STATE_IMMUNE, // ignoring input, waiting fo HW to settle
+ STATE_WAITING_FOR_SIGNAL, // looking for a loud signal
+ STATE_WAITING_FOR_LOCK, // trying to lock onto the phase of the sine
+ STATE_LOCKED, // locked on the sine wave, looking for glitches
+ STATE_GLITCHING, // locked on the sine wave but glitching
+ NUM_STATES
+ };
+
+ enum constants {
+ // Arbitrary durations, assuming 48000 Hz
+ IDLE_FRAME_COUNT = 48 * 100,
+ IMMUNE_FRAME_COUNT = 48 * 100,
+ PERIODS_NEEDED_FOR_LOCK = 8,
+ MIN_SNR_DB = 65
+ };
+
+ static constexpr float kNoiseAmplitude = 0.00; // Used to experiment with warbling caused by DRC.
+ static constexpr int kTargetGlitchFrequency = 607;
+ static constexpr double kMaxPhaseError = M_PI * 0.05;
+
+ float mTolerance = 0.10; // scaled from 0.0 to 1.0
+ double mThreshold = 0.005;
+ int mSinePeriod = 1; // this will be set before use
+ double mInverseSinePeriod = 1.0;
+
+ int32_t mStateFrameCounters[NUM_STATES];
+
+ double mPhaseIncrement = 0.0;
+ double mInputPhase = 0.0;
+ double mOutputPhase = 0.0;
+ double mMagnitude = 0.0;
+ int32_t mFramesAccumulated = 0;
+ double mSinAccumulator = 0.0;
+ double mCosAccumulator = 0.0;
+ double mMaxGlitchDelta = 0.0;
+ int32_t mGlitchCount = 0;
+ int32_t mNonGlitchCount = 0;
+ int32_t mGlitchLength = 0;
+ // This is used for processing every frame so we cache it here.
+ double mScaledTolerance = 0.0;
+ int mDownCounter = IDLE_FRAME_COUNT;
+ int32_t mFrameCounter = 0;
+ double mOutputAmplitude = 0.75;
+
+ int32_t mForceGlitchDuration = 0; // if > 0 then force a glitch for debugging
+ int32_t mForceGlitchCounter = 4 * 48000; // count down and trigger at zero
+
+ // measure background noise continuously as a deviation from the expected signal
+ double mSumSquareSignal = 0.0;
+ double mSumSquareNoise = 0.0;
+ double mMeanSquareSignal = 0.0;
+ double mMeanSquareNoise = 0.0;
+
+ PeakDetector mPeakFollower;
+
+ PseudoRandom mWhiteNoise;
+
+ sine_state_t mState = STATE_IDLE;
+};
+
+
+#endif //ANALYZER_GLITCH_ANALYZER_H
diff --git a/media/libaaudio/examples/loopback/src/analyzer/LatencyAnalyzer.h b/media/libaaudio/examples/loopback/src/analyzer/LatencyAnalyzer.h
new file mode 100644
index 0000000..e506791
--- /dev/null
+++ b/media/libaaudio/examples/loopback/src/analyzer/LatencyAnalyzer.h
@@ -0,0 +1,606 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/**
+ * Tools for measuring latency and for detecting glitches.
+ * These classes are pure math and can be used with any audio system.
+ */
+
+#ifndef ANALYZER_LATENCY_ANALYZER_H
+#define ANALYZER_LATENCY_ANALYZER_H
+
+#include <algorithm>
+#include <assert.h>
+#include <cctype>
+#include <iomanip>
+#include <iostream>
+#include <math.h>
+#include <memory>
+#include <sstream>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <vector>
+
+#include "PeakDetector.h"
+#include "PseudoRandom.h"
+#include "RandomPulseGenerator.h"
+
+// This is used when the code is in Oboe.
+#ifndef ALOGD
+#define ALOGD printf
+#define ALOGE printf
+#define ALOGW printf
+#endif
+
+#define LOOPBACK_RESULT_TAG "RESULT: "
+
+static constexpr int32_t kDefaultSampleRate = 48000;
+static constexpr int32_t kMillisPerSecond = 1000;
+static constexpr int32_t kMaxLatencyMillis = 700; // arbitrary and generous
+static constexpr double kMinimumConfidence = 0.2;
+
+struct LatencyReport {
+ int32_t latencyInFrames = 0.0;
+ double confidence = 0.0;
+
+ void reset() {
+ latencyInFrames = 0;
+ confidence = 0.0;
+ }
+};
+
+// Calculate a normalized cross correlation.
+static double calculateNormalizedCorrelation(const float *a,
+ const float *b,
+ int windowSize) {
+ double correlation = 0.0;
+ double sumProducts = 0.0;
+ double sumSquares = 0.0;
+
+ // Correlate a against b.
+ for (int i = 0; i < windowSize; i++) {
+ float s1 = a[i];
+ float s2 = b[i];
+ // Use a normalized cross-correlation.
+ sumProducts += s1 * s2;
+ sumSquares += ((s1 * s1) + (s2 * s2));
+ }
+
+ if (sumSquares >= 1.0e-9) {
+ correlation = 2.0 * sumProducts / sumSquares;
+ }
+ return correlation;
+}
+
+static double calculateRootMeanSquare(float *data, int32_t numSamples) {
+ double sum = 0.0;
+ for (int32_t i = 0; i < numSamples; i++) {
+ float sample = data[i];
+ sum += sample * sample;
+ }
+ return sqrt(sum / numSamples);
+}
+
+/**
+ * Monophonic recording with processing.
+ */
+class AudioRecording
+{
+public:
+
+ void allocate(int maxFrames) {
+ mData = std::make_unique<float[]>(maxFrames);
+ mMaxFrames = maxFrames;
+ }
+
+ // Write SHORT data from the first channel.
+ int32_t write(int16_t *inputData, int32_t inputChannelCount, int32_t numFrames) {
+ // stop at end of buffer
+ if ((mFrameCounter + numFrames) > mMaxFrames) {
+ numFrames = mMaxFrames - mFrameCounter;
+ }
+ for (int i = 0; i < numFrames; i++) {
+ mData[mFrameCounter++] = inputData[i * inputChannelCount] * (1.0f / 32768);
+ }
+ return numFrames;
+ }
+
+ // Write FLOAT data from the first channel.
+ int32_t write(float *inputData, int32_t inputChannelCount, int32_t numFrames) {
+ // stop at end of buffer
+ if ((mFrameCounter + numFrames) > mMaxFrames) {
+ numFrames = mMaxFrames - mFrameCounter;
+ }
+ for (int i = 0; i < numFrames; i++) {
+ mData[mFrameCounter++] = inputData[i * inputChannelCount];
+ }
+ return numFrames;
+ }
+
+ // Write FLOAT data from the first channel.
+ int32_t write(float sample) {
+ // stop at end of buffer
+ if (mFrameCounter < mMaxFrames) {
+ mData[mFrameCounter++] = sample;
+ return 1;
+ }
+ return 0;
+ }
+
+ void clear() {
+ mFrameCounter = 0;
+ }
+ int32_t size() const {
+ return mFrameCounter;
+ }
+
+ bool isFull() const {
+ return mFrameCounter >= mMaxFrames;
+ }
+
+ float *getData() const {
+ return mData.get();
+ }
+
+ void setSampleRate(int32_t sampleRate) {
+ mSampleRate = sampleRate;
+ }
+
+ int32_t getSampleRate() const {
+ return mSampleRate;
+ }
+
+ /**
+ * Square the samples so they are all positive and so the peaks are emphasized.
+ */
+ void square() {
+ float *x = mData.get();
+ for (int i = 0; i < mFrameCounter; i++) {
+ x[i] *= x[i];
+ }
+ }
+
+ /**
+ * Amplify a signal so that the peak matches the specified target.
+ *
+ * @param target final max value
+ * @return gain applied to signal
+ */
+ float normalize(float target) {
+ float maxValue = 1.0e-9f;
+ for (int i = 0; i < mFrameCounter; i++) {
+ maxValue = std::max(maxValue, abs(mData[i]));
+ }
+ float gain = target / maxValue;
+ for (int i = 0; i < mFrameCounter; i++) {
+ mData[i] *= gain;
+ }
+ return gain;
+ }
+
+private:
+ std::unique_ptr<float[]> mData;
+ int32_t mFrameCounter = 0;
+ int32_t mMaxFrames = 0;
+ int32_t mSampleRate = kDefaultSampleRate; // common default
+};
+
+static int measureLatencyFromPulse(AudioRecording &recorded,
+ AudioRecording &pulse,
+ LatencyReport *report) {
+
+ report->latencyInFrames = 0;
+ report->confidence = 0.0;
+
+ int numCorrelations = recorded.size() - pulse.size();
+ if (numCorrelations < 10) {
+ ALOGE("%s() recording too small = %d frames\n", __func__, recorded.size());
+ return -1;
+ }
+ std::unique_ptr<float[]> correlations= std::make_unique<float[]>(numCorrelations);
+
+ // Correlate pulse against the recorded data.
+ for (int i = 0; i < numCorrelations; i++) {
+ float correlation = (float) calculateNormalizedCorrelation(&recorded.getData()[i],
+ &pulse.getData()[0],
+ pulse.size());
+ correlations[i] = correlation;
+ }
+
+ // Find highest peak in correlation array.
+ float peakCorrelation = 0.0;
+ int peakIndex = -1;
+ for (int i = 0; i < numCorrelations; i++) {
+ float value = abs(correlations[i]);
+ if (value > peakCorrelation) {
+ peakCorrelation = value;
+ peakIndex = i;
+ }
+ }
+ if (peakIndex < 0) {
+ ALOGE("%s() no signal for correlation\n", __func__);
+ return -2;
+ }
+
+ report->latencyInFrames = peakIndex;
+ report->confidence = peakCorrelation;
+
+ return 0;
+}
+
+// ====================================================================================
+class LoopbackProcessor {
+public:
+ virtual ~LoopbackProcessor() = default;
+
+ enum result_code {
+ RESULT_OK = 0,
+ ERROR_NOISY = -99,
+ ERROR_VOLUME_TOO_LOW,
+ ERROR_VOLUME_TOO_HIGH,
+ ERROR_CONFIDENCE,
+ ERROR_INVALID_STATE,
+ ERROR_GLITCHES,
+ ERROR_NO_LOCK
+ };
+
+ virtual void prepareToTest() {
+ reset();
+ }
+
+ virtual void reset() {
+ mResult = 0;
+ mResetCount++;
+ }
+
+ virtual result_code processInputFrame(float *frameData, int channelCount) = 0;
+ virtual result_code processOutputFrame(float *frameData, int channelCount) = 0;
+
+ void process(float *inputData, int inputChannelCount, int numInputFrames,
+ float *outputData, int outputChannelCount, int numOutputFrames) {
+ int numBoth = std::min(numInputFrames, numOutputFrames);
+ // Process one frame at a time.
+ for (int i = 0; i < numBoth; i++) {
+ processInputFrame(inputData, inputChannelCount);
+ inputData += inputChannelCount;
+ processOutputFrame(outputData, outputChannelCount);
+ outputData += outputChannelCount;
+ }
+ // If there is more input than output.
+ for (int i = numBoth; i < numInputFrames; i++) {
+ processInputFrame(inputData, inputChannelCount);
+ inputData += inputChannelCount;
+ }
+ // If there is more output than input.
+ for (int i = numBoth; i < numOutputFrames; i++) {
+ processOutputFrame(outputData, outputChannelCount);
+ outputData += outputChannelCount;
+ }
+ }
+
+ virtual std::string analyze() = 0;
+
+ virtual void printStatus() {};
+
+ int32_t getResult() {
+ return mResult;
+ }
+
+ void setResult(int32_t result) {
+ mResult = result;
+ }
+
+ virtual bool isDone() {
+ return false;
+ }
+
+ virtual int save(const char *fileName) {
+ (void) fileName;
+ return -1;
+ }
+
+ virtual int load(const char *fileName) {
+ (void) fileName;
+ return -1;
+ }
+
+ virtual void setSampleRate(int32_t sampleRate) {
+ mSampleRate = sampleRate;
+ }
+
+ int32_t getSampleRate() const {
+ return mSampleRate;
+ }
+
+ int32_t getResetCount() const {
+ return mResetCount;
+ }
+
+ /** Called when not enough input frames could be read after synchronization.
+ */
+ virtual void onInsufficientRead() {
+ reset();
+ }
+
+protected:
+ int32_t mResetCount = 0;
+
+private:
+ int32_t mSampleRate = kDefaultSampleRate;
+ int32_t mResult = 0;
+};
+
+class LatencyAnalyzer : public LoopbackProcessor {
+public:
+
+ LatencyAnalyzer() : LoopbackProcessor() {}
+ virtual ~LatencyAnalyzer() = default;
+
+ virtual int32_t getProgress() const = 0;
+
+ virtual int getState() = 0;
+
+ // @return latency in frames
+ virtual int32_t getMeasuredLatency() = 0;
+
+ virtual double getMeasuredConfidence() = 0;
+
+ virtual double getBackgroundRMS() = 0;
+
+ virtual double getSignalRMS() = 0;
+
+};
+
+// ====================================================================================
+/**
+ * Measure latency given a loopback stream data.
+ * Use an encoded bit train as the sound source because it
+ * has an unambiguous correlation value.
+ * Uses a state machine to cycle through various stages.
+ *
+ */
+class PulseLatencyAnalyzer : public LatencyAnalyzer {
+public:
+
+ PulseLatencyAnalyzer() : LatencyAnalyzer() {
+ int32_t maxLatencyFrames = getSampleRate() * kMaxLatencyMillis / kMillisPerSecond;
+ int32_t numPulseBits = getSampleRate() * kPulseLengthMillis
+ / (kFramesPerEncodedBit * kMillisPerSecond);
+ int32_t pulseLength = numPulseBits * kFramesPerEncodedBit;
+ mFramesToRecord = pulseLength + maxLatencyFrames;
+ mAudioRecording.allocate(mFramesToRecord);
+ mAudioRecording.setSampleRate(getSampleRate());
+ generateRandomPulse(pulseLength);
+ }
+
+ void generateRandomPulse(int32_t pulseLength) {
+ mPulse.allocate(pulseLength);
+ RandomPulseGenerator pulser(kFramesPerEncodedBit);
+ for (int i = 0; i < pulseLength; i++) {
+ mPulse.write(pulser.nextFloat());
+ }
+ }
+
+ int getState() override {
+ return mState;
+ }
+
+ void setSampleRate(int32_t sampleRate) override {
+ LoopbackProcessor::setSampleRate(sampleRate);
+ mAudioRecording.setSampleRate(sampleRate);
+ }
+
+ void reset() override {
+ LoopbackProcessor::reset();
+ mDownCounter = getSampleRate() / 2;
+ mLoopCounter = 0;
+
+ mPulseCursor = 0;
+ mBackgroundSumSquare = 0.0f;
+ mBackgroundSumCount = 0;
+ mBackgroundRMS = 0.0f;
+ mSignalRMS = 0.0f;
+
+ mState = STATE_MEASURE_BACKGROUND;
+ mAudioRecording.clear();
+ mLatencyReport.reset();
+ }
+
+ bool hasEnoughData() {
+ return mAudioRecording.isFull();
+ }
+
+ bool isDone() override {
+ return mState == STATE_DONE;
+ }
+
+ int32_t getProgress() const override {
+ return mAudioRecording.size();
+ }
+
+ std::string analyze() override {
+ std::stringstream report;
+ report << "PulseLatencyAnalyzer ---------------\n";
+ report << LOOPBACK_RESULT_TAG "test.state = "
+ << std::setw(8) << mState << "\n";
+ report << LOOPBACK_RESULT_TAG "test.state.name = "
+ << convertStateToText(mState) << "\n";
+ report << LOOPBACK_RESULT_TAG "background.rms = "
+ << std::setw(8) << mBackgroundRMS << "\n";
+
+ int32_t newResult = RESULT_OK;
+ if (mState != STATE_GOT_DATA) {
+ report << "WARNING - Bad state. Check volume on device.\n";
+ // setResult(ERROR_INVALID_STATE);
+ } else {
+ float gain = mAudioRecording.normalize(1.0f);
+ measureLatencyFromPulse(mAudioRecording,
+ mPulse,
+ &mLatencyReport);
+
+ if (mLatencyReport.confidence < kMinimumConfidence) {
+ report << " ERROR - confidence too low!";
+ newResult = ERROR_CONFIDENCE;
+ } else {
+ mSignalRMS = calculateRootMeanSquare(
+ &mAudioRecording.getData()[mLatencyReport.latencyInFrames], mPulse.size())
+ / gain;
+ }
+ double latencyMillis = kMillisPerSecond * (double) mLatencyReport.latencyInFrames
+ / getSampleRate();
+ report << LOOPBACK_RESULT_TAG "latency.frames = " << std::setw(8)
+ << mLatencyReport.latencyInFrames << "\n";
+ report << LOOPBACK_RESULT_TAG "latency.msec = " << std::setw(8)
+ << latencyMillis << "\n";
+ report << LOOPBACK_RESULT_TAG "latency.confidence = " << std::setw(8)
+ << mLatencyReport.confidence << "\n";
+ }
+ mState = STATE_DONE;
+ if (getResult() == RESULT_OK) {
+ setResult(newResult);
+ }
+
+ return report.str();
+ }
+
+ int32_t getMeasuredLatency() override {
+ return mLatencyReport.latencyInFrames;
+ }
+
+ double getMeasuredConfidence() override {
+ return mLatencyReport.confidence;
+ }
+
+ double getBackgroundRMS() override {
+ return mBackgroundRMS;
+ }
+
+ double getSignalRMS() override {
+ return mSignalRMS;
+ }
+
+ void printStatus() override {
+ ALOGD("st = %d", mState);
+ }
+
+ result_code processInputFrame(float *frameData, int channelCount) override {
+ echo_state nextState = mState;
+ mLoopCounter++;
+
+ switch (mState) {
+ case STATE_MEASURE_BACKGROUND:
+ // Measure background RMS on channel 0
+ mBackgroundSumSquare += frameData[0] * frameData[0];
+ mBackgroundSumCount++;
+ mDownCounter--;
+ if (mDownCounter <= 0) {
+ mBackgroundRMS = sqrtf(mBackgroundSumSquare / mBackgroundSumCount);
+ nextState = STATE_IN_PULSE;
+ mPulseCursor = 0;
+ }
+ break;
+
+ case STATE_IN_PULSE:
+ // Record input until the mAudioRecording is full.
+ mAudioRecording.write(frameData, channelCount, 1);
+ if (hasEnoughData()) {
+ nextState = STATE_GOT_DATA;
+ }
+ break;
+
+ case STATE_GOT_DATA:
+ case STATE_DONE:
+ default:
+ break;
+ }
+
+ mState = nextState;
+ return RESULT_OK;
+ }
+
+ result_code processOutputFrame(float *frameData, int channelCount) override {
+ switch (mState) {
+ case STATE_IN_PULSE:
+ if (mPulseCursor < mPulse.size()) {
+ float pulseSample = mPulse.getData()[mPulseCursor++];
+ for (int i = 0; i < channelCount; i++) {
+ frameData[i] = pulseSample;
+ }
+ } else {
+ for (int i = 0; i < channelCount; i++) {
+ frameData[i] = 0;
+ }
+ }
+ break;
+
+ case STATE_MEASURE_BACKGROUND:
+ case STATE_GOT_DATA:
+ case STATE_DONE:
+ default:
+ for (int i = 0; i < channelCount; i++) {
+ frameData[i] = 0.0f; // silence
+ }
+ break;
+ }
+
+ return RESULT_OK;
+ }
+
+private:
+
+ enum echo_state {
+ STATE_MEASURE_BACKGROUND,
+ STATE_IN_PULSE,
+ STATE_GOT_DATA, // must match RoundTripLatencyActivity.java
+ STATE_DONE,
+ };
+
+ const char *convertStateToText(echo_state state) {
+ switch (state) {
+ case STATE_MEASURE_BACKGROUND:
+ return "INIT";
+ case STATE_IN_PULSE:
+ return "PULSE";
+ case STATE_GOT_DATA:
+ return "GOT_DATA";
+ case STATE_DONE:
+ return "DONE";
+ }
+ return "UNKNOWN";
+ }
+
+ int32_t mDownCounter = 500;
+ int32_t mLoopCounter = 0;
+ echo_state mState = STATE_MEASURE_BACKGROUND;
+
+ static constexpr int32_t kFramesPerEncodedBit = 8; // multiple of 2
+ static constexpr int32_t kPulseLengthMillis = 500;
+
+ AudioRecording mPulse;
+ int32_t mPulseCursor = 0;
+
+ double mBackgroundSumSquare = 0.0;
+ int32_t mBackgroundSumCount = 0;
+ double mBackgroundRMS = 0.0;
+ double mSignalRMS = 0.0;
+ int32_t mFramesToRecord = 0;
+
+ AudioRecording mAudioRecording; // contains only the input after starting the pulse
+ LatencyReport mLatencyReport;
+};
+
+#endif // ANALYZER_LATENCY_ANALYZER_H
diff --git a/media/libaaudio/examples/loopback/src/analyzer/ManchesterEncoder.h b/media/libaaudio/examples/loopback/src/analyzer/ManchesterEncoder.h
new file mode 100644
index 0000000..0a4bd5b
--- /dev/null
+++ b/media/libaaudio/examples/loopback/src/analyzer/ManchesterEncoder.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANALYZER_MANCHESTER_ENCODER_H
+#define ANALYZER_MANCHESTER_ENCODER_H
+
+#include <cstdint>
+
+/**
+ * Encode bytes using Manchester Coding scheme.
+ *
+ * Manchester Code is self clocking.
+ * There is a transition in the middle of every bit.
+ * Zero is high then low.
+ * One is low then high.
+ *
+ * This avoids having long DC sections that would droop when
+ * passed though analog circuits with AC coupling.
+ *
+ * IEEE 802.3 compatible.
+ */
+
+class ManchesterEncoder {
+public:
+ ManchesterEncoder(int samplesPerPulse)
+ : mSamplesPerPulse(samplesPerPulse)
+ , mSamplesPerPulseHalf(samplesPerPulse / 2)
+ , mCursor(samplesPerPulse) {
+ }
+
+ virtual ~ManchesterEncoder() = default;
+
+ /**
+ * This will be called when the next byte is needed.
+ * @return
+ */
+ virtual uint8_t onNextByte() = 0;
+
+ /**
+ * Generate the next floating point sample.
+ * @return
+ */
+ virtual float nextFloat() {
+ advanceSample();
+ if (mCurrentBit) {
+ return (mCursor < mSamplesPerPulseHalf) ? -1.0f : 1.0f; // one
+ } else {
+ return (mCursor < mSamplesPerPulseHalf) ? 1.0f : -1.0f; // zero
+ }
+ }
+
+protected:
+ /**
+ * This will be called when a new bit is ready to be encoded.
+ * It can be used to prepare the encoded samples.
+ * @param current
+ */
+ virtual void onNextBit(bool /* current */) {};
+
+ void advanceSample() {
+ // Are we ready for a new bit?
+ if (++mCursor >= mSamplesPerPulse) {
+ mCursor = 0;
+ if (mBitsLeft == 0) {
+ mCurrentByte = onNextByte();
+ mBitsLeft = 8;
+ }
+ --mBitsLeft;
+ mCurrentBit = (mCurrentByte >> mBitsLeft) & 1;
+ onNextBit(mCurrentBit);
+ }
+ }
+
+ bool getCurrentBit() {
+ return mCurrentBit;
+ }
+
+ const int mSamplesPerPulse;
+ const int mSamplesPerPulseHalf;
+ int mCursor;
+ int mBitsLeft = 0;
+ uint8_t mCurrentByte = 0;
+ bool mCurrentBit = false;
+};
+#endif //ANALYZER_MANCHESTER_ENCODER_H
diff --git a/media/libaaudio/examples/loopback/src/analyzer/PeakDetector.h b/media/libaaudio/examples/loopback/src/analyzer/PeakDetector.h
new file mode 100644
index 0000000..4b3b4e7
--- /dev/null
+++ b/media/libaaudio/examples/loopback/src/analyzer/PeakDetector.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANALYZER_PEAK_DETECTOR_H
+#define ANALYZER_PEAK_DETECTOR_H
+
+#include <math.h>
+
+/**
+ * Measure a peak envelope by rising with the peaks,
+ * and decaying exponentially after each peak.
+ * The absolute value of the input signal is used.
+ */
+class PeakDetector {
+public:
+
+ void reset() {
+ mLevel = 0.0;
+ }
+
+ double process(double input) {
+ mLevel *= mDecay; // exponential decay
+ input = fabs(input);
+ // never fall below the input signal
+ if (input > mLevel) {
+ mLevel = input;
+ }
+ return mLevel;
+ }
+
+ double getLevel() const {
+ return mLevel;
+ }
+
+ double getDecay() const {
+ return mDecay;
+ }
+
+ /**
+ * Multiply the level by this amount on every iteration.
+ * This provides an exponential decay curve.
+ * A value just under 1.0 is best, for example, 0.99;
+ * @param decay scale level for each input
+ */
+ void setDecay(double decay) {
+ mDecay = decay;
+ }
+
+private:
+ static constexpr double kDefaultDecay = 0.99f;
+
+ double mLevel = 0.0;
+ double mDecay = kDefaultDecay;
+};
+#endif //ANALYZER_PEAK_DETECTOR_H
diff --git a/media/libaaudio/examples/loopback/src/analyzer/PseudoRandom.h b/media/libaaudio/examples/loopback/src/analyzer/PseudoRandom.h
new file mode 100644
index 0000000..1c4938c
--- /dev/null
+++ b/media/libaaudio/examples/loopback/src/analyzer/PseudoRandom.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANALYZER_PSEUDORANDOM_H
+#define ANALYZER_PSEUDORANDOM_H
+
+#include <cctype>
+
+class PseudoRandom {
+public:
+ PseudoRandom(int64_t seed = 99887766)
+ : mSeed(seed)
+ {}
+
+ /**
+ * Returns the next random double from -1.0 to 1.0
+ *
+ * @return value from -1.0 to 1.0
+ */
+ double nextRandomDouble() {
+ return nextRandomInteger() * (0.5 / (((int32_t)1) << 30));
+ }
+
+ /** Calculate random 32 bit number using linear-congruential method
+ * with known real-time performance.
+ */
+ int32_t nextRandomInteger() {
+#if __has_builtin(__builtin_mul_overflow) && __has_builtin(__builtin_add_overflow)
+ int64_t prod;
+ // Use values for 64-bit sequence from MMIX by Donald Knuth.
+ __builtin_mul_overflow(mSeed, (int64_t)6364136223846793005, &prod);
+ __builtin_add_overflow(prod, (int64_t)1442695040888963407, &mSeed);
+#else
+ mSeed = (mSeed * (int64_t)6364136223846793005) + (int64_t)1442695040888963407;
+#endif
+ return (int32_t) (mSeed >> 32); // The higher bits have a longer sequence.
+ }
+
+private:
+ int64_t mSeed;
+};
+
+#endif //ANALYZER_PSEUDORANDOM_H
diff --git a/media/libaaudio/examples/loopback/src/analyzer/RandomPulseGenerator.h b/media/libaaudio/examples/loopback/src/analyzer/RandomPulseGenerator.h
new file mode 100644
index 0000000..030050b
--- /dev/null
+++ b/media/libaaudio/examples/loopback/src/analyzer/RandomPulseGenerator.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANALYZER_RANDOM_PULSE_GENERATOR_H
+#define ANALYZER_RANDOM_PULSE_GENERATOR_H
+
+#include <stdlib.h>
+#include "RoundedManchesterEncoder.h"
+
+/**
+ * Encode random ones and zeros using Manchester Code per IEEE 802.3.
+ */
+class RandomPulseGenerator : public RoundedManchesterEncoder {
+public:
+ RandomPulseGenerator(int samplesPerPulse)
+ : RoundedManchesterEncoder(samplesPerPulse) {
+ }
+
+ virtual ~RandomPulseGenerator() = default;
+
+ /**
+ * This will be called when the next byte is needed.
+ * @return random byte
+ */
+ uint8_t onNextByte() override {
+ return static_cast<uint8_t>(rand());
+ }
+};
+
+#endif //ANALYZER_RANDOM_PULSE_GENERATOR_H
diff --git a/media/libaaudio/examples/loopback/src/analyzer/RoundedManchesterEncoder.h b/media/libaaudio/examples/loopback/src/analyzer/RoundedManchesterEncoder.h
new file mode 100644
index 0000000..f2eba84
--- /dev/null
+++ b/media/libaaudio/examples/loopback/src/analyzer/RoundedManchesterEncoder.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANALYZER_ROUNDED_MANCHESTER_ENCODER_H
+#define ANALYZER_ROUNDED_MANCHESTER_ENCODER_H
+
+#include <math.h>
+#include <memory.h>
+#include <stdlib.h>
+#include "ManchesterEncoder.h"
+
+/**
+ * Encode bytes using Manchester Code.
+ * Round the edges using a half cosine to reduce ringing caused by a hard edge.
+ */
+
+class RoundedManchesterEncoder : public ManchesterEncoder {
+public:
+ RoundedManchesterEncoder(int samplesPerPulse)
+ : ManchesterEncoder(samplesPerPulse) {
+ int rampSize = samplesPerPulse / 4;
+ mZeroAfterZero = std::make_unique<float[]>(samplesPerPulse);
+ mZeroAfterOne = std::make_unique<float[]>(samplesPerPulse);
+
+ int sampleIndex = 0;
+ for (int rampIndex = 0; rampIndex < rampSize; rampIndex++) {
+ float phase = (rampIndex + 1) * M_PI / rampSize;
+ float sample = -cosf(phase);
+ mZeroAfterZero[sampleIndex] = sample;
+ mZeroAfterOne[sampleIndex] = 1.0f;
+ sampleIndex++;
+ }
+ for (int rampIndex = 0; rampIndex < rampSize; rampIndex++) {
+ mZeroAfterZero[sampleIndex] = 1.0f;
+ mZeroAfterOne[sampleIndex] = 1.0f;
+ sampleIndex++;
+ }
+ for (int rampIndex = 0; rampIndex < rampSize; rampIndex++) {
+ float phase = (rampIndex + 1) * M_PI / rampSize;
+ float sample = cosf(phase);
+ mZeroAfterZero[sampleIndex] = sample;
+ mZeroAfterOne[sampleIndex] = sample;
+ sampleIndex++;
+ }
+ for (int rampIndex = 0; rampIndex < rampSize; rampIndex++) {
+ mZeroAfterZero[sampleIndex] = -1.0f;
+ mZeroAfterOne[sampleIndex] = -1.0f;
+ sampleIndex++;
+ }
+ }
+
+ void onNextBit(bool current) override {
+ // Do we need to use the rounded edge?
+ mCurrentSamples = (current ^ mPreviousBit)
+ ? mZeroAfterOne.get()
+ : mZeroAfterZero.get();
+ mPreviousBit = current;
+ }
+
+ float nextFloat() override {
+ advanceSample();
+ float output = mCurrentSamples[mCursor];
+ if (getCurrentBit()) output = -output;
+ return output;
+ }
+
+private:
+
+ bool mPreviousBit = false;
+ float *mCurrentSamples = nullptr;
+ std::unique_ptr<float[]> mZeroAfterZero;
+ std::unique_ptr<float[]> mZeroAfterOne;
+};
+
+#endif //ANALYZER_ROUNDED_MANCHESTER_ENCODER_H
diff --git a/media/libaaudio/examples/loopback/src/loopback.cpp b/media/libaaudio/examples/loopback/src/loopback.cpp
index 49d921f..0d2ec70 100644
--- a/media/libaaudio/examples/loopback/src/loopback.cpp
+++ b/media/libaaudio/examples/loopback/src/loopback.cpp
@@ -20,6 +20,8 @@
#include <assert.h>
#include <cctype>
#include <errno.h>
+#include <iomanip>
+#include <iostream>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
@@ -33,7 +35,9 @@
#include "AAudioSimplePlayer.h"
#include "AAudioSimpleRecorder.h"
#include "AAudioExampleUtils.h"
-#include "LoopbackAnalyzer.h"
+
+#include "analyzer/GlitchAnalyzer.h"
+#include "analyzer/LatencyAnalyzer.h"
#include "../../utils/AAudioExampleUtils.h"
// V0.4.00 = rectify and low-pass filter the echos, auto-correlate entire echo
@@ -41,7 +45,8 @@
// fix -n option to set output buffer for -tm
// plot first glitch
// V0.4.02 = allow -n0 for minimal buffer size
-#define APP_VERSION "0.4.02"
+// V0.5.00 = use latency analyzer from OboeTester, uses random noise for latency
+#define APP_VERSION "0.5.00"
// Tag for machine readable results as property = value pairs
#define RESULT_TAG "RESULT: "
@@ -57,6 +62,20 @@
constexpr int kDefaultHangTimeMillis = 50;
constexpr int kMaxGlitchEventsToSave = 32;
+static void printAudioScope(float sample) {
+ const int maxStars = 80; // arbitrary, fits on one line
+ char c = '*';
+ if (sample < -1.0) {
+ sample = -1.0;
+ c = '$';
+ } else if (sample > 1.0) {
+ sample = 1.0;
+ c = '$';
+ }
+ int numSpaces = (int) (((sample + 1.0) * 0.5) * maxStars);
+ printf("%*c%c\n", numSpaces, ' ', c);
+}
+
struct LoopbackData {
AAudioStream *inputStream = nullptr;
AAudioStream *outputStream = nullptr;
@@ -83,8 +102,8 @@
aaudio_result_t inputError = AAUDIO_OK;
aaudio_result_t outputError = AAUDIO_OK;
- SineAnalyzer sineAnalyzer;
- EchoAnalyzer echoAnalyzer;
+ GlitchAnalyzer sineAnalyzer;
+ PulseLatencyAnalyzer echoAnalyzer;
AudioRecording audioRecording;
LoopbackProcessor *loopbackProcessor;
@@ -254,17 +273,18 @@
}
// Analyze the data.
- LoopbackProcessor::process_result procResult = myData->loopbackProcessor->process(myData->inputFloatData,
+ myData->loopbackProcessor->process(myData->inputFloatData,
myData->actualInputChannelCount,
+ numFrames,
outputData,
myData->actualOutputChannelCount,
numFrames);
-
- if (procResult == LoopbackProcessor::PROCESS_RESULT_GLITCH) {
- if (myData->numGlitchEvents < kMaxGlitchEventsToSave) {
- myData->glitchFrames[myData->numGlitchEvents++] = myData->audioRecording.size();
- }
- }
+//
+// if (procResult == LoopbackProcessor::PROCESS_RESULT_GLITCH) {
+// if (myData->numGlitchEvents < kMaxGlitchEventsToSave) {
+// myData->glitchFrames[myData->numGlitchEvents++] = myData->audioRecording.size();
+// }
+// }
// Save for later.
myData->audioRecording.write(myData->inputFloatData,
@@ -283,8 +303,8 @@
}
static void MyErrorCallbackProc(
- AAudioStream *stream __unused,
- void *userData __unused,
+ AAudioStream * /* stream */,
+ void * userData,
aaudio_result_t error) {
printf("Error Callback, error: %d\n",(int)error);
LoopbackData *myData = (LoopbackData *) userData;
@@ -305,8 +325,8 @@
printf(" l for _LATENCY\n");
printf(" p for _POWER_SAVING\n");
printf(" -t{test} select test mode\n");
- printf(" m for sine magnitude\n");
- printf(" e for echo latency (default)\n");
+ printf(" g for Glitch detection\n");
+ printf(" l for round trip Latency (default)\n");
printf(" f for file latency, analyzes %s\n\n", FILENAME_ECHOS);
printf(" -X use EXCLUSIVE mode for input\n");
printf("Example: aaudio_loopback -n2 -pl -Pl -x\n");
@@ -333,20 +353,22 @@
}
enum {
- TEST_SINE_MAGNITUDE = 0,
- TEST_ECHO_LATENCY,
+ TEST_GLITCHES = 0,
+ TEST_LATENCY,
TEST_FILE_LATENCY,
};
static int parseTestMode(char c) {
- int testMode = TEST_ECHO_LATENCY;
+ int testMode = TEST_LATENCY;
c = tolower(c);
switch (c) {
- case 'm':
- testMode = TEST_SINE_MAGNITUDE;
+ case 'm': // deprecated
+ case 'g':
+ testMode = TEST_GLITCHES;
break;
- case 'e':
- testMode = TEST_ECHO_LATENCY;
+ case 'e': // deprecated
+ case 'l':
+ testMode = TEST_LATENCY;
break;
case 'f':
testMode = TEST_FILE_LATENCY;
@@ -408,9 +430,10 @@
int32_t actualSampleRate = 0;
int written = 0;
- int testMode = TEST_ECHO_LATENCY;
+ int testMode = TEST_LATENCY;
double gain = 1.0;
int hangTimeMillis = 0;
+ std::string report;
// Make printf print immediately so that debug info is not stuck
// in a buffer if we hang or crash.
@@ -488,22 +511,21 @@
int32_t requestedOutputBursts = argParser.getNumberOfBursts();
switch(testMode) {
- case TEST_SINE_MAGNITUDE:
+ case TEST_GLITCHES:
loopbackData.loopbackProcessor = &loopbackData.sineAnalyzer;
break;
- case TEST_ECHO_LATENCY:
- loopbackData.echoAnalyzer.setGain(gain);
+ case TEST_LATENCY:
+ // TODO loopbackData.echoAnalyzer.setGain(gain);
loopbackData.loopbackProcessor = &loopbackData.echoAnalyzer;
break;
case TEST_FILE_LATENCY: {
- loopbackData.echoAnalyzer.setGain(gain);
-
+ // TODO loopbackData.echoAnalyzer.setGain(gain);
loopbackData.loopbackProcessor = &loopbackData.echoAnalyzer;
int read = loopbackData.loopbackProcessor->load(FILENAME_ECHOS);
printf("main() read %d mono samples from %s on Android device, rate = %d\n",
read, FILENAME_ECHOS,
loopbackData.loopbackProcessor->getSampleRate());
- loopbackData.loopbackProcessor->report();
+ std::cout << loopbackData.loopbackProcessor->analyze();
goto report_result;
}
break;
@@ -557,7 +579,7 @@
int32_t actualCapacity = AAudioStream_getBufferCapacityInFrames(inputStream);
(void) AAudioStream_setBufferSizeInFrames(inputStream, actualCapacity);
- if (testMode == TEST_SINE_MAGNITUDE
+ if (testMode == TEST_GLITCHES
&& requestedOutputBursts == AAUDIO_UNSPECIFIED) {
result = AAudioStream_setBufferSizeInFrames(outputStream, actualCapacity);
if (result < 0) {
@@ -594,10 +616,10 @@
loopbackData.inputFloatData = new float[loopbackData.inputFramesMaximum *
loopbackData.actualInputChannelCount]{};
- loopbackData.loopbackProcessor->reset();
-
loopbackData.hangTimeMillis = hangTimeMillis;
+ loopbackData.loopbackProcessor->prepareToTest();
+
// Start OUTPUT first so INPUT does not overflow.
result = player.start();
if (result != AAUDIO_OK) {
@@ -669,7 +691,8 @@
printf("input error = %d = %s\n",
loopbackData.inputError, AAudio_convertResultToText(loopbackData.inputError));
-
+/*
+ // TODO Restore this code some day if we want to save files.
written = loopbackData.loopbackProcessor->save(FILENAME_ECHOS);
if (written > 0) {
printf("main() wrote %8d mono samples to \"%s\" on Android device\n",
@@ -681,9 +704,9 @@
printf("main() wrote %8d mono samples to \"%s\" on Android device\n",
written, FILENAME_ALL);
}
-
+*/
if (loopbackData.inputError == AAUDIO_OK) {
- if (testMode == TEST_SINE_MAGNITUDE) {
+ if (testMode == TEST_GLITCHES) {
if (loopbackData.numGlitchEvents > 0) {
// Graph around the first glitch if there is one.
const int32_t start = loopbackData.glitchFrames[0] - 8;
@@ -697,7 +720,8 @@
}
}
- loopbackData.loopbackProcessor->report();
+ std::cout << "Please wait several seconds for analysis to complete.\n";
+ std::cout << loopbackData.loopbackProcessor->analyze();
}
{
diff --git a/media/libaaudio/examples/write_sine/Android.bp b/media/libaaudio/examples/write_sine/Android.bp
index cc80861..aa25e67 100644
--- a/media/libaaudio/examples/write_sine/Android.bp
+++ b/media/libaaudio/examples/write_sine/Android.bp
@@ -4,7 +4,6 @@
cflags: ["-Wall", "-Werror"],
shared_libs: ["libaaudio"],
header_libs: ["libaaudio_example_utils"],
- pack_relocations: false,
}
cc_test {
@@ -13,5 +12,4 @@
cflags: ["-Wall", "-Werror"],
shared_libs: ["libaaudio"],
header_libs: ["libaaudio_example_utils"],
- pack_relocations: false,
}
diff --git a/media/libaaudio/include/aaudio/AAudio.h b/media/libaaudio/include/aaudio/AAudio.h
index 5bebd61..2f43b22 100644
--- a/media/libaaudio/include/aaudio/AAudio.h
+++ b/media/libaaudio/include/aaudio/AAudio.h
@@ -227,6 +227,8 @@
};
typedef int32_t aaudio_performance_mode_t;
+#define AAUDIO_SYSTEM_USAGE_OFFSET 1000
+
/**
* The USAGE attribute expresses "why" you are playing a sound, what is this sound used for.
* This information is used by certain platforms or routing policies
@@ -297,7 +299,31 @@
/**
* Use this for audio responses to user queries, audio instructions or help utterances.
*/
- AAUDIO_USAGE_ASSISTANT = 16
+ AAUDIO_USAGE_ASSISTANT = 16,
+
+ /**
+ * Use this in case of playing sounds in an emergency.
+ * Privileged MODIFY_AUDIO_ROUTING permission required.
+ */
+ AAUDIO_SYSTEM_USAGE_EMERGENCY = AAUDIO_SYSTEM_USAGE_OFFSET,
+
+ /**
+ * Use this for safety sounds and alerts, for example backup camera obstacle detection.
+ * Privileged MODIFY_AUDIO_ROUTING permission required.
+ */
+ AAUDIO_SYSTEM_USAGE_SAFETY = AAUDIO_SYSTEM_USAGE_OFFSET + 1,
+
+ /**
+ * Use this for vehicle status alerts and information, for example the check engine light.
+ * Privileged MODIFY_AUDIO_ROUTING permission required.
+ */
+ AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS = AAUDIO_SYSTEM_USAGE_OFFSET + 2,
+
+ /**
+ * Use this for traffic announcements, etc.
+ * Privileged MODIFY_AUDIO_ROUTING permission required.
+ */
+ AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT = AAUDIO_SYSTEM_USAGE_OFFSET + 3,
};
typedef int32_t aaudio_usage_t;
@@ -995,10 +1021,26 @@
// Stream Control
// ============================================================
+#if __ANDROID_API__ >= 30
/**
- * Free the resources associated with a stream created by AAudioStreamBuilder_openStream()
+ * Free the audio resources associated with a stream created by
+ * AAudioStreamBuilder_openStream().
+ * AAudioStream_close() should be called at some point after calling
+ * this function.
*
- * Available since API level 26.
+ * After this call, the stream will be in {@link #AAUDIO_STREAM_STATE_CLOSING}
+ *
+ * @param stream reference provided by AAudioStreamBuilder_openStream()
+ * @return {@link #AAUDIO_OK} or a negative error.
+ */
+AAUDIO_API aaudio_result_t AAudioStream_release(AAudioStream* stream) __INTRODUCED_IN(30);
+#endif // __ANDROID_API__
+
+/**
+ * Delete the internal data structures associated with the stream created
+ * by AAudioStreamBuilder_openStream().
+ *
+ * If AAudioStream_release() has not been called then it will be called automatically.
*
* @param stream reference provided by AAudioStreamBuilder_openStream()
* @return {@link #AAUDIO_OK} or a negative error.
diff --git a/media/libaaudio/src/Android.bp b/media/libaaudio/src/Android.bp
index baada22..901c28f 100644
--- a/media/libaaudio/src/Android.bp
+++ b/media/libaaudio/src/Android.bp
@@ -44,14 +44,6 @@
sanitize: {
integer_overflow: true,
misc_undefined: ["bounds"],
- diag: {
- integer_overflow: true,
- misc_undefined: ["bounds"],
- no_recover: [
- "bounds",
- "integer",
- ],
- },
},
stubs: {
@@ -76,6 +68,7 @@
header_libs: [
"libaaudio_headers",
"libmedia_headers",
+ "libmediametrics_headers",
],
export_header_lib_headers: ["libaaudio_headers"],
@@ -137,14 +130,5 @@
sanitize: {
integer_overflow: true,
misc_undefined: ["bounds"],
- diag: {
- integer_overflow: true,
- misc_undefined: ["bounds"],
- no_recover: [
- "bounds",
- "integer",
- ],
- },
},
-
}
diff --git a/media/libaaudio/src/binding/IAAudioService.cpp b/media/libaaudio/src/binding/IAAudioService.cpp
index 97ad2b0..e017b3a 100644
--- a/media/libaaudio/src/binding/IAAudioService.cpp
+++ b/media/libaaudio/src/binding/IAAudioService.cpp
@@ -237,12 +237,12 @@
status_t BnAAudioService::onTransact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags) {
- aaudio_handle_t streamHandle;
+ aaudio_handle_t streamHandle = 0;
aaudio::AAudioStreamRequest request;
aaudio::AAudioStreamConfiguration configuration;
- pid_t tid;
- int64_t nanoseconds;
- aaudio_result_t result;
+ pid_t tid = 0;
+ int64_t nanoseconds = 0;
+ aaudio_result_t result = AAUDIO_OK;
status_t status = NO_ERROR;
ALOGV("BnAAudioService::onTransact(%i) %i", code, flags);
@@ -285,7 +285,11 @@
case CLOSE_STREAM: {
CHECK_INTERFACE(IAAudioService, data, reply);
- data.readInt32(&streamHandle);
+ status = data.readInt32(&streamHandle);
+ if (status != NO_ERROR) {
+ ALOGE("BnAAudioService::%s(CLOSE_STREAM) streamHandle failed!", __func__);
+ return status;
+ }
result = closeStream(streamHandle);
//ALOGD("BnAAudioService::onTransact CLOSE_STREAM 0x%08X, result = %d",
// streamHandle, result);
@@ -297,6 +301,7 @@
CHECK_INTERFACE(IAAudioService, data, reply);
status = data.readInt32(&streamHandle);
if (status != NO_ERROR) {
+ ALOGE("BnAAudioService::%s(GET_STREAM_DESCRIPTION) streamHandle failed!", __func__);
return status;
}
aaudio::AudioEndpointParcelable parcelable;
@@ -313,7 +318,11 @@
case START_STREAM: {
CHECK_INTERFACE(IAAudioService, data, reply);
- data.readInt32(&streamHandle);
+ status = data.readInt32(&streamHandle);
+ if (status != NO_ERROR) {
+ ALOGE("BnAAudioService::%s(START_STREAM) streamHandle failed!", __func__);
+ return status;
+ }
result = startStream(streamHandle);
ALOGV("BnAAudioService::onTransact START_STREAM 0x%08X, result = %d",
streamHandle, result);
@@ -323,7 +332,11 @@
case PAUSE_STREAM: {
CHECK_INTERFACE(IAAudioService, data, reply);
- data.readInt32(&streamHandle);
+ status = data.readInt32(&streamHandle);
+ if (status != NO_ERROR) {
+ ALOGE("BnAAudioService::%s(PAUSE_STREAM) streamHandle failed!", __func__);
+ return status;
+ }
result = pauseStream(streamHandle);
ALOGV("BnAAudioService::onTransact PAUSE_STREAM 0x%08X, result = %d",
streamHandle, result);
@@ -333,7 +346,11 @@
case STOP_STREAM: {
CHECK_INTERFACE(IAAudioService, data, reply);
- data.readInt32(&streamHandle);
+ status = data.readInt32(&streamHandle);
+ if (status != NO_ERROR) {
+ ALOGE("BnAAudioService::%s(STOP_STREAM) streamHandle failed!", __func__);
+ return status;
+ }
result = stopStream(streamHandle);
ALOGV("BnAAudioService::onTransact STOP_STREAM 0x%08X, result = %d",
streamHandle, result);
@@ -343,7 +360,11 @@
case FLUSH_STREAM: {
CHECK_INTERFACE(IAAudioService, data, reply);
- data.readInt32(&streamHandle);
+ status = data.readInt32(&streamHandle);
+ if (status != NO_ERROR) {
+ ALOGE("BnAAudioService::%s(FLUSH_STREAM) streamHandle failed!", __func__);
+ return status;
+ }
result = flushStream(streamHandle);
ALOGV("BnAAudioService::onTransact FLUSH_STREAM 0x%08X, result = %d",
streamHandle, result);
@@ -353,20 +374,40 @@
case REGISTER_AUDIO_THREAD: {
CHECK_INTERFACE(IAAudioService, data, reply);
- data.readInt32(&streamHandle);
- data.readInt32(&tid);
- data.readInt64(&nanoseconds);
+ status = data.readInt32(&streamHandle);
+ if (status != NO_ERROR) {
+ ALOGE("BnAAudioService::%s(REGISTER_AUDIO_THREAD) streamHandle failed!", __func__);
+ return status;
+ }
+ status = data.readInt32(&tid);
+ if (status != NO_ERROR) {
+ ALOGE("BnAAudioService::%s(REGISTER_AUDIO_THREAD) tid failed!", __func__);
+ return status;
+ }
+ status = data.readInt64(&nanoseconds);
+ if (status != NO_ERROR) {
+ ALOGE("BnAAudioService::%s(REGISTER_AUDIO_THREAD) nanoseconds failed!", __func__);
+ return status;
+ }
result = registerAudioThread(streamHandle, tid, nanoseconds);
- ALOGV("BnAAudioService::onTransact REGISTER_AUDIO_THREAD 0x%08X, result = %d",
- streamHandle, result);
+ ALOGV("BnAAudioService::%s(REGISTER_AUDIO_THREAD) 0x%08X, result = %d",
+ __func__, streamHandle, result);
reply->writeInt32(result);
return NO_ERROR;
} break;
case UNREGISTER_AUDIO_THREAD: {
CHECK_INTERFACE(IAAudioService, data, reply);
- data.readInt32(&streamHandle);
- data.readInt32(&tid);
+ status = data.readInt32(&streamHandle);
+ if (status != NO_ERROR) {
+ ALOGE("BnAAudioService::%s(UNREGISTER_AUDIO_THREAD) streamHandle failed!", __func__);
+ return status;
+ }
+ status = data.readInt32(&tid);
+ if (status != NO_ERROR) {
+ ALOGE("BnAAudioService::%s(UNREGISTER_AUDIO_THREAD) tid failed!", __func__);
+ return status;
+ }
result = unregisterAudioThread(streamHandle, tid);
ALOGV("BnAAudioService::onTransact UNREGISTER_AUDIO_THREAD 0x%08X, result = %d",
streamHandle, result);
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index bfad254..b6548e6 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -258,18 +258,17 @@
return result;
error:
- close();
+ releaseCloseFinal();
return result;
}
// This must be called under mStreamLock.
-aaudio_result_t AudioStreamInternal::close() {
+aaudio_result_t AudioStreamInternal::release_l() {
aaudio_result_t result = AAUDIO_OK;
ALOGV("%s(): mServiceStreamHandle = 0x%08X", __func__, mServiceStreamHandle);
if (mServiceStreamHandle != AAUDIO_HANDLE_INVALID) {
- // Don't close a stream while it is running.
aaudio_stream_state_t currentState = getState();
- // Don't close a stream while it is running. Stop it first.
+ // Don't release a stream while it is running. Stop it first.
// If DISCONNECTED then we should still try to stop in case the
// error callback is still running.
if (isActive() || currentState == AAUDIO_STREAM_STATE_DISCONNECTED) {
@@ -282,10 +281,8 @@
mServiceInterface.closeStream(serviceStreamHandle);
delete[] mCallbackBuffer;
mCallbackBuffer = nullptr;
-
- setState(AAUDIO_STREAM_STATE_CLOSED);
result = mEndPointParcelable.close();
- aaudio_result_t result2 = AudioStream::close();
+ aaudio_result_t result2 = AudioStream::release_l();
return (result != AAUDIO_OK) ? result : result2;
} else {
return AAUDIO_ERROR_INVALID_HANDLE;
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index 596d37f..8843a8a 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -58,7 +58,7 @@
aaudio_result_t open(const AudioStreamBuilder &builder) override;
- aaudio_result_t close() override;
+ aaudio_result_t release_l() override;
aaudio_result_t setBufferSize(int32_t requestedFrames) override;
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index dc9f48c..536009a 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -49,7 +49,7 @@
getDeviceChannelCount());
if (result != AAUDIO_OK) {
- close();
+ releaseCloseFinal();
}
// Sample rate is constrained to common values by now and should not overflow.
int32_t numFrames = kRampMSec * getSampleRate() / AAUDIO_MILLIS_PER_SECOND;
diff --git a/media/libaaudio/src/core/AAudioAudio.cpp b/media/libaaudio/src/core/AAudioAudio.cpp
index 184e9cb..8965875 100644
--- a/media/libaaudio/src/core/AAudioAudio.cpp
+++ b/media/libaaudio/src/core/AAudioAudio.cpp
@@ -25,7 +25,6 @@
#include <aaudio/AAudio.h>
#include <aaudio/AAudioTesting.h>
-
#include "AudioClock.h"
#include "AudioGlobal.h"
#include "AudioStreamBuilder.h"
@@ -231,21 +230,42 @@
return AAUDIO_ERROR_NULL;
}
-AAUDIO_API aaudio_result_t AAudioStream_close(AAudioStream* stream)
-{
+AAUDIO_API aaudio_result_t AAudioStream_release(AAudioStream* stream) {
aaudio_result_t result = AAUDIO_ERROR_NULL;
- AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
+ AudioStream* audioStream = convertAAudioStreamToAudioStream(stream);
if (audioStream != nullptr) {
aaudio_stream_id_t id = audioStream->getId();
ALOGD("%s(s#%u) called ---------------", __func__, id);
- result = audioStream->safeClose();
- // Close will only fail if called illegally, for example, from a callback.
+ result = audioStream->safeRelease();
+ // safeRelease() will only fail if called illegally, for example, from a callback.
+ // That would result in the release of an active stream, which would cause a crash.
+ if (result != AAUDIO_OK) {
+ ALOGW("%s(s#%u) failed. Release it from another thread.",
+ __func__, id);
+ }
+ ALOGD("%s(s#%u) returned %d %s ---------", __func__,
+ id, result, AAudio_convertResultToText(result));
+ }
+ return result;
+}
+
+AAUDIO_API aaudio_result_t AAudioStream_close(AAudioStream* stream) {
+ aaudio_result_t result = AAUDIO_ERROR_NULL;
+ AudioStream* audioStream = convertAAudioStreamToAudioStream(stream);
+ if (audioStream != nullptr) {
+ aaudio_stream_id_t id = audioStream->getId();
+ ALOGD("%s(s#%u) called ---------------", __func__, id);
+ result = audioStream->safeRelease();
+ // safeRelease will only fail if called illegally, for example, from a callback.
// That would result in deleting an active stream, which would cause a crash.
- if (result == AAUDIO_OK) {
- audioStream->unregisterPlayerBase();
- delete audioStream;
+ if (result != AAUDIO_OK) {
+ ALOGW("%s(s#%u) failed. Close it from another thread.",
+ __func__, id);
} else {
- ALOGW("%s attempt to close failed. Close it from another thread.", __func__);
+ audioStream->unregisterPlayerBase();
+ // Mark CLOSED to keep destructors from asserting.
+ audioStream->closeFinal();
+ delete audioStream;
}
ALOGD("%s(s#%u) returned %d ---------", __func__, id, result);
}
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.cpp b/media/libaaudio/src/core/AAudioStreamParameters.cpp
index 58058f5..5f45261 100644
--- a/media/libaaudio/src/core/AAudioStreamParameters.cpp
+++ b/media/libaaudio/src/core/AAudioStreamParameters.cpp
@@ -133,6 +133,10 @@
case AAUDIO_USAGE_ASSISTANCE_SONIFICATION:
case AAUDIO_USAGE_GAME:
case AAUDIO_USAGE_ASSISTANT:
+ case AAUDIO_SYSTEM_USAGE_EMERGENCY:
+ case AAUDIO_SYSTEM_USAGE_SAFETY:
+ case AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS:
+ case AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT:
break; // valid
default:
ALOGD("usage not valid = %d", mUsage);
diff --git a/media/libaaudio/src/core/AudioGlobal.cpp b/media/libaaudio/src/core/AudioGlobal.cpp
index e6d9a0d..d299761 100644
--- a/media/libaaudio/src/core/AudioGlobal.cpp
+++ b/media/libaaudio/src/core/AudioGlobal.cpp
@@ -87,9 +87,9 @@
AAUDIO_CASE_ENUM(AAUDIO_STREAM_STATE_FLUSHED);
AAUDIO_CASE_ENUM(AAUDIO_STREAM_STATE_STOPPING);
AAUDIO_CASE_ENUM(AAUDIO_STREAM_STATE_STOPPED);
- AAUDIO_CASE_ENUM(AAUDIO_STREAM_STATE_DISCONNECTED);
AAUDIO_CASE_ENUM(AAUDIO_STREAM_STATE_CLOSING);
AAUDIO_CASE_ENUM(AAUDIO_STREAM_STATE_CLOSED);
+ AAUDIO_CASE_ENUM(AAUDIO_STREAM_STATE_DISCONNECTED);
}
return "Unrecognized AAudio state.";
}
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index 1560e0c..f51db70 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -249,25 +249,29 @@
return requestStop();
}
-aaudio_result_t AudioStream::safeClose() {
- // This get temporarily unlocked in the close when joining callback threads.
+aaudio_result_t AudioStream::safeRelease() {
+ // This get temporarily unlocked in the release() when joining callback threads.
std::lock_guard<std::mutex> lock(mStreamLock);
if (collidesWithCallback()) {
ALOGE("%s cannot be called from a callback!", __func__);
return AAUDIO_ERROR_INVALID_STATE;
}
- return close();
+ if (getState() == AAUDIO_STREAM_STATE_CLOSING) {
+ return AAUDIO_OK;
+ }
+ return release_l();
}
void AudioStream::setState(aaudio_stream_state_t state) {
- ALOGV("%s(%d) from %d to %d", __func__, getId(), mState, state);
+ ALOGD("%s(s#%d) from %d to %d", __func__, getId(), mState, state);
// CLOSED is a final state
if (mState == AAUDIO_STREAM_STATE_CLOSED) {
ALOGE("%s(%d) tried to set to %d but already CLOSED", __func__, getId(), state);
- // Once DISCONNECTED, we can only move to CLOSED state.
+ // Once DISCONNECTED, we can only move to CLOSING or CLOSED state.
} else if (mState == AAUDIO_STREAM_STATE_DISCONNECTED
- && state != AAUDIO_STREAM_STATE_CLOSED) {
+ && !(state == AAUDIO_STREAM_STATE_CLOSING
+ || state == AAUDIO_STREAM_STATE_CLOSED)) {
ALOGE("%s(%d) tried to set to %d but already DISCONNECTED", __func__, getId(), state);
} else {
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index b4ffcf2..9bda41b 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -115,13 +115,32 @@
virtual aaudio_result_t open(const AudioStreamBuilder& builder);
/**
- * Close the stream and deallocate any resources from the open() call.
- * It is safe to call close() multiple times.
+ * Free any hardware or system resources from the open() call.
+ * It is safe to call release_l() multiple times.
*/
- virtual aaudio_result_t close() {
+ virtual aaudio_result_t release_l() {
+ setState(AAUDIO_STREAM_STATE_CLOSING);
return AAUDIO_OK;
}
+ aaudio_result_t closeFinal() {
+ // State is checked by destructor.
+ setState(AAUDIO_STREAM_STATE_CLOSED);
+ return AAUDIO_OK;
+ }
+
+ /**
+ * Release then close the stream.
+ * @return AAUDIO_OK or negative error.
+ */
+ aaudio_result_t releaseCloseFinal() {
+ aaudio_result_t result = release_l(); // TODO review locking
+ if (result == AAUDIO_OK) {
+ result = closeFinal();
+ }
+ return result;
+ }
+
// This is only used to identify a stream in the logs without
// revealing any pointers.
aaudio_stream_id_t getId() {
@@ -373,7 +392,7 @@
*/
aaudio_result_t systemStopFromCallback();
- aaudio_result_t safeClose();
+ aaudio_result_t safeRelease();
protected:
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.cpp b/media/libaaudio/src/core/AudioStreamBuilder.cpp
index af28a59..3a1e1c9 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.cpp
+++ b/media/libaaudio/src/core/AudioStreamBuilder.cpp
@@ -142,14 +142,14 @@
// TODO Support other performance settings in MMAP mode.
// Disable MMAP if low latency not requested.
if (getPerformanceMode() != AAUDIO_PERFORMANCE_MODE_LOW_LATENCY) {
- ALOGD("%s() MMAP not available because AAUDIO_PERFORMANCE_MODE_LOW_LATENCY not used.",
+ ALOGD("%s() MMAP not used because AAUDIO_PERFORMANCE_MODE_LOW_LATENCY not requested.",
__func__);
allowMMap = false;
}
// SessionID and Effects are only supported in Legacy mode.
if (getSessionId() != AAUDIO_SESSION_ID_NONE) {
- ALOGD("%s() MMAP not available because sessionId used.", __func__);
+ ALOGD("%s() MMAP not used because sessionId specified.", __func__);
allowMMap = false;
}
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index 54af580..2ed82d2 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -182,7 +182,7 @@
// Did we get a valid track?
status_t status = mAudioRecord->initCheck();
if (status != OK) {
- close();
+ releaseCloseFinal();
ALOGE("open(), initCheck() returned %d", status);
return AAudioConvert_androidToAAudioResult(status);
}
@@ -278,16 +278,17 @@
return AAUDIO_OK;
}
-aaudio_result_t AudioStreamRecord::close()
-{
- // TODO add close() or release() to AudioRecord API then call it from here
- if (getState() != AAUDIO_STREAM_STATE_CLOSED) {
+aaudio_result_t AudioStreamRecord::release_l() {
+ // TODO add close() or release() to AudioFlinger's AudioRecord API.
+ // Then call it from here
+ if (getState() != AAUDIO_STREAM_STATE_CLOSING) {
mAudioRecord->removeAudioDeviceCallback(mDeviceCallback);
mAudioRecord.clear();
- setState(AAUDIO_STREAM_STATE_CLOSED);
+ mFixedBlockWriter.close();
+ return AudioStream::release_l();
+ } else {
+ return AAUDIO_OK; // already released
}
- mFixedBlockWriter.close();
- return AudioStream::close();
}
const void * AudioStreamRecord::maybeConvertDeviceData(const void *audioData, int32_t numFrames) {
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.h b/media/libaaudio/src/legacy/AudioStreamRecord.h
index 2f41d34..c5944c7 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.h
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.h
@@ -38,7 +38,7 @@
virtual ~AudioStreamRecord();
aaudio_result_t open(const AudioStreamBuilder & builder) override;
- aaudio_result_t close() override;
+ aaudio_result_t release_l() override;
aaudio_result_t requestStart() override;
aaudio_result_t requestStop() override;
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index 094cdd1..00963d6 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -176,7 +176,7 @@
// Did we get a valid track?
status_t status = mAudioTrack->initCheck();
if (status != NO_ERROR) {
- close();
+ releaseCloseFinal();
ALOGE("open(), initCheck() returned %d", status);
return AAudioConvert_androidToAAudioResult(status);
}
@@ -239,14 +239,18 @@
return AAUDIO_OK;
}
-aaudio_result_t AudioStreamTrack::close()
-{
- if (getState() != AAUDIO_STREAM_STATE_CLOSED) {
+aaudio_result_t AudioStreamTrack::release_l() {
+ if (getState() != AAUDIO_STREAM_STATE_CLOSING) {
mAudioTrack->removeAudioDeviceCallback(mDeviceCallback);
- setState(AAUDIO_STREAM_STATE_CLOSED);
+ // TODO Investigate why clear() causes a hang in test_various.cpp
+ // if I call close() from a data callback.
+ // But the same thing in AudioRecord is OK!
+ // mAudioTrack.clear();
+ mFixedBlockReader.close();
+ return AudioStream::release_l();
+ } else {
+ return AAUDIO_OK; // already released
}
- mFixedBlockReader.close();
- return AAUDIO_OK;
}
void AudioStreamTrack::processCallback(int event, void *info) {
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.h b/media/libaaudio/src/legacy/AudioStreamTrack.h
index 68608de..550f693 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.h
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.h
@@ -41,7 +41,7 @@
aaudio_result_t open(const AudioStreamBuilder & builder) override;
- aaudio_result_t close() override;
+ aaudio_result_t release_l() override;
aaudio_result_t requestStart() override;
aaudio_result_t requestPause() override;
diff --git a/media/libaaudio/src/libaaudio.map.txt b/media/libaaudio/src/libaaudio.map.txt
index a87ede3..2e00aa5 100644
--- a/media/libaaudio/src/libaaudio.map.txt
+++ b/media/libaaudio/src/libaaudio.map.txt
@@ -22,6 +22,7 @@
AAudioStreamBuilder_setInputPreset; # introduced=28
AAudioStreamBuilder_setAllowedCapturePolicy; # introduced=29
AAudioStreamBuilder_setSessionId; # introduced=28
+ AAudioStreamBuilder_setPrivacySensitive; # introduced=30
AAudioStreamBuilder_openStream;
AAudioStreamBuilder_delete;
AAudioStream_close;
@@ -56,6 +57,8 @@
AAudioStream_getSessionId; # introduced=28
AAudioStream_getTimestamp;
AAudioStream_isMMapUsed;
+ AAudioStream_isPrivacySensitive; # introduced=30
+ AAudioStream_release; # introduced=30
local:
*;
};
diff --git a/media/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp
index ef89697..9007b10 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.cpp
+++ b/media/libaaudio/src/utility/AAudioUtilities.cpp
@@ -183,6 +183,10 @@
STATIC_ASSERT(AAUDIO_USAGE_ASSISTANCE_SONIFICATION == AUDIO_USAGE_ASSISTANCE_SONIFICATION);
STATIC_ASSERT(AAUDIO_USAGE_GAME == AUDIO_USAGE_GAME);
STATIC_ASSERT(AAUDIO_USAGE_ASSISTANT == AUDIO_USAGE_ASSISTANT);
+ STATIC_ASSERT(AAUDIO_SYSTEM_USAGE_EMERGENCY == AUDIO_USAGE_EMERGENCY);
+ STATIC_ASSERT(AAUDIO_SYSTEM_USAGE_SAFETY == AUDIO_USAGE_SAFETY);
+ STATIC_ASSERT(AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS == AUDIO_USAGE_VEHICLE_STATUS);
+ STATIC_ASSERT(AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT == AUDIO_USAGE_ANNOUNCEMENT);
if (usage == AAUDIO_UNSPECIFIED) {
usage = AAUDIO_USAGE_MEDIA;
}
diff --git a/media/libaaudio/tests/test_attributes.cpp b/media/libaaudio/tests/test_attributes.cpp
index 32ee2a3..d540866 100644
--- a/media/libaaudio/tests/test_attributes.cpp
+++ b/media/libaaudio/tests/test_attributes.cpp
@@ -33,6 +33,7 @@
aaudio_content_type_t contentType,
aaudio_input_preset_t preset = DONT_SET,
aaudio_allowed_capture_policy_t capturePolicy = DONT_SET,
+ int privacyMode = DONT_SET,
aaudio_direction_t direction = AAUDIO_DIRECTION_OUTPUT) {
float *buffer = new float[kNumFrames * kChannelCount];
@@ -60,6 +61,9 @@
if (capturePolicy != DONT_SET) {
AAudioStreamBuilder_setAllowedCapturePolicy(aaudioBuilder, capturePolicy);
}
+ if (privacyMode != DONT_SET) {
+ AAudioStreamBuilder_setPrivacySensitive(aaudioBuilder, (bool)privacyMode);
+ }
// Create an AAudioStream using the Builder.
ASSERT_EQ(AAUDIO_OK, AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream));
@@ -90,6 +94,13 @@
: preset;
EXPECT_EQ(expectedCapturePolicy, AAudioStream_getAllowedCapturePolicy(aaudioStream));
+ bool expectedPrivacyMode =
+ (privacyMode == DONT_SET) ?
+ ((preset == AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION
+ || preset == AAUDIO_INPUT_PRESET_CAMCORDER) ? true : false) :
+ privacyMode;
+ EXPECT_EQ(expectedPrivacyMode, AAudioStream_isPrivacySensitive(aaudioStream));
+
EXPECT_EQ(AAUDIO_OK, AAudioStream_requestStart(aaudioStream));
if (direction == AAUDIO_DIRECTION_INPUT) {
@@ -120,7 +131,11 @@
AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,
AAUDIO_USAGE_ASSISTANCE_SONIFICATION,
AAUDIO_USAGE_GAME,
- AAUDIO_USAGE_ASSISTANT
+ AAUDIO_USAGE_ASSISTANT,
+ AAUDIO_SYSTEM_USAGE_EMERGENCY,
+ AAUDIO_SYSTEM_USAGE_SAFETY,
+ AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS,
+ AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT
};
static const aaudio_content_type_t sContentypes[] = {
@@ -151,6 +166,12 @@
AAUDIO_ALLOW_CAPTURE_BY_NONE,
};
+static const int sPrivacyModes[] = {
+ DONT_SET,
+ false,
+ true,
+};
+
static void checkAttributesUsage(aaudio_performance_mode_t perfMode) {
for (aaudio_usage_t usage : sUsages) {
checkAttributes(perfMode, usage, DONT_SET);
@@ -170,6 +191,7 @@
DONT_SET,
inputPreset,
DONT_SET,
+ DONT_SET,
AAUDIO_DIRECTION_INPUT);
}
}
@@ -185,6 +207,18 @@
}
}
+static void checkAttributesPrivacySensitive(aaudio_performance_mode_t perfMode) {
+ for (int privacyMode : sPrivacyModes) {
+ checkAttributes(perfMode,
+ DONT_SET,
+ DONT_SET,
+ DONT_SET,
+ DONT_SET,
+ privacyMode,
+ AAUDIO_DIRECTION_INPUT);
+ }
+}
+
TEST(test_attributes, aaudio_usage_perfnone) {
checkAttributesUsage(AAUDIO_PERFORMANCE_MODE_NONE);
}
@@ -216,3 +250,7 @@
TEST(test_attributes, aaudio_allowed_capture_policy_lowlat) {
checkAttributesAllowedCapturePolicy(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
}
+
+TEST(test_attributes, aaudio_allowed_privacy_sensitive_lowlat) {
+ checkAttributesPrivacySensitive(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+}
diff --git a/media/libaaudio/tests/test_various.cpp b/media/libaaudio/tests/test_various.cpp
index 4b065c9..5bb1046 100644
--- a/media/libaaudio/tests/test_various.cpp
+++ b/media/libaaudio/tests/test_various.cpp
@@ -40,10 +40,62 @@
return AAUDIO_CALLBACK_RESULT_CONTINUE;
}
-// Test AAudioStream_setBufferSizeInFrames()
-
constexpr int64_t NANOS_PER_MILLISECOND = 1000 * 1000;
+void checkReleaseThenClose(aaudio_performance_mode_t perfMode,
+ aaudio_sharing_mode_t sharingMode) {
+ AAudioStreamBuilder* aaudioBuilder = nullptr;
+ AAudioStream* aaudioStream = nullptr;
+
+ // Use an AAudioStreamBuilder to contain requested parameters.
+ ASSERT_EQ(AAUDIO_OK, AAudio_createStreamBuilder(&aaudioBuilder));
+
+ // Request stream properties.
+ AAudioStreamBuilder_setDataCallback(aaudioBuilder,
+ NoopDataCallbackProc,
+ nullptr);
+ AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, perfMode);
+ AAudioStreamBuilder_setSharingMode(aaudioBuilder, sharingMode);
+
+ // Create an AAudioStream using the Builder.
+ ASSERT_EQ(AAUDIO_OK,
+ AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream));
+ AAudioStreamBuilder_delete(aaudioBuilder);
+
+ ASSERT_EQ(AAUDIO_OK, AAudioStream_requestStart(aaudioStream));
+
+ sleep(1);
+
+ EXPECT_EQ(AAUDIO_OK, AAudioStream_requestStop(aaudioStream));
+
+ EXPECT_EQ(AAUDIO_OK, AAudioStream_release(aaudioStream));
+ aaudio_stream_state_t state = AAudioStream_getState(aaudioStream);
+ EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, state);
+
+ // We should be able to call this again without crashing.
+ EXPECT_EQ(AAUDIO_OK, AAudioStream_release(aaudioStream));
+ state = AAudioStream_getState(aaudioStream);
+ EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, state);
+
+ EXPECT_EQ(AAUDIO_OK, AAudioStream_close(aaudioStream));
+}
+
+TEST(test_various, aaudio_release_close_none) {
+ checkReleaseThenClose(AAUDIO_PERFORMANCE_MODE_NONE,
+ AAUDIO_SHARING_MODE_SHARED);
+ // No EXCLUSIVE streams with MODE_NONE.
+}
+
+TEST(test_various, aaudio_release_close_low_shared) {
+ checkReleaseThenClose(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+ AAUDIO_SHARING_MODE_SHARED);
+}
+
+TEST(test_various, aaudio_release_close_low_exclusive) {
+ checkReleaseThenClose(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+ AAUDIO_SHARING_MODE_EXCLUSIVE);
+}
+
enum FunctionToCall {
CALL_START, CALL_STOP, CALL_PAUSE, CALL_FLUSH
};
@@ -441,7 +493,7 @@
bufferCapacity, bufferCapacity % framesPerBurst);
actualSize = AAudioStream_setBufferSizeInFrames(aaudioStream, 0);
- EXPECT_GT(actualSize, 0);
+ EXPECT_GE(actualSize, 0); // 0 is legal in R
EXPECT_LE(actualSize, bufferCapacity);
actualSize = AAudioStream_setBufferSizeInFrames(aaudioStream, 2 * framesPerBurst);
@@ -469,7 +521,7 @@
EXPECT_LE(actualSize, bufferCapacity);
actualSize = AAudioStream_setBufferSizeInFrames(aaudioStream, INT32_MIN);
- EXPECT_GT(actualSize, 0);
+ EXPECT_GE(actualSize, 0); // 0 is legal in R
EXPECT_LE(actualSize, bufferCapacity);
AAudioStream_close(aaudioStream);
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index d1812e6..3638d2c 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -90,7 +90,12 @@
],
export_shared_lib_headers: ["libbinder"],
- local_include_dirs: ["include/media", "aidl"],
+ include_dirs: [
+ "frameworks/av/media/libnbaio/include_mono/",
+ ],
+ local_include_dirs: [
+ "include/media", "aidl"
+ ],
header_libs: [
"libaudioclient_headers",
"libbase_headers",
diff --git a/media/libaudioclient/AudioEffect.cpp b/media/libaudioclient/AudioEffect.cpp
index 7d5b690..a1b141b 100644
--- a/media/libaudioclient/AudioEffect.cpp
+++ b/media/libaudioclient/AudioEffect.cpp
@@ -138,6 +138,7 @@
mIEffectClient = new EffectClient(this);
mClientPid = IPCThreadState::self()->getCallingPid();
+ mClientUid = IPCThreadState::self()->getCallingUid();
iEffect = audioFlinger->createEffect((effect_descriptor_t *)&mDescriptor,
mIEffectClient, priority, io, mSessionId, device, mOpPackageName, mClientPid,
@@ -179,7 +180,7 @@
mStatus, mEnabled, mClientPid);
if (!audio_is_global_session(mSessionId)) {
- AudioSystem::acquireAudioSessionId(mSessionId, mClientPid);
+ AudioSystem::acquireAudioSessionId(mSessionId, mClientPid, mClientUid);
}
return mStatus;
diff --git a/media/libaudioclient/AudioPolicy.cpp b/media/libaudioclient/AudioPolicy.cpp
index 06fc23c..d468239 100644
--- a/media/libaudioclient/AudioPolicy.cpp
+++ b/media/libaudioclient/AudioPolicy.cpp
@@ -53,6 +53,10 @@
case RULE_EXCLUDE_UID:
mValue.mUid = (uid_t) parcel->readInt32();
break;
+ case RULE_MATCH_USERID:
+ case RULE_EXCLUDE_USERID:
+ mValue.mUserId = (int) parcel->readInt32();
+ break;
default:
ALOGE("Trying to build AudioMixMatchCriterion from unknown rule %d", mRule);
return BAD_VALUE;
@@ -163,9 +167,50 @@
return false;
}
+void AudioMix::setExcludeUserId(int userId) const {
+ AudioMixMatchCriterion crit;
+ crit.mRule = RULE_EXCLUDE_USERID;
+ crit.mValue.mUserId = userId;
+ mCriteria.add(crit);
+}
+
+void AudioMix::setMatchUserId(int userId) const {
+ AudioMixMatchCriterion crit;
+ crit.mRule = RULE_MATCH_USERID;
+ crit.mValue.mUserId = userId;
+ mCriteria.add(crit);
+}
+
+bool AudioMix::hasUserIdRule(bool match, int userId) const {
+ const uint32_t rule = match ? RULE_MATCH_USERID : RULE_EXCLUDE_USERID;
+ for (size_t i = 0; i < mCriteria.size(); i++) {
+ if (mCriteria[i].mRule == rule
+ && mCriteria[i].mValue.mUserId == userId) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool AudioMix::hasMatchUserIdRule() const {
+ for (size_t i = 0; i < mCriteria.size(); i++) {
+ if (mCriteria[i].mRule == RULE_MATCH_USERID) {
+ return true;
+ }
+ }
+ return false;
+}
+
bool AudioMix::isDeviceAffinityCompatible() const {
return ((mMixType == MIX_TYPE_PLAYERS)
&& (mRouteFlags == MIX_ROUTE_FLAG_RENDER));
}
+bool AudioMix::hasMatchingRuleForUsage(std::function<bool (audio_usage_t)>const& func) const {
+ return std::any_of(mCriteria.begin(), mCriteria.end(), [func](auto& criterion) {
+ return criterion.mRule == RULE_MATCH_ATTRIBUTE_USAGE
+ && func(criterion.mValue.mUsage);
+ });
+}
+
} // namespace android
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index c22db59..f9d1798 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -32,7 +32,7 @@
#include <private/media/AudioTrackShared.h>
#include <processgroup/sched_policy.h>
#include <media/IAudioFlinger.h>
-#include <media/MediaAnalyticsItem.h>
+#include <media/MediaMetricsItem.h>
#include <media/TypeConverter.h>
#define WAIT_PERIOD_MS 10
@@ -79,37 +79,41 @@
#define MM_PREFIX "android.media.audiorecord." // avoid cut-n-paste errors.
// Java API 28 entries, do not change.
- mAnalyticsItem->setCString(MM_PREFIX "encoding", toString(record->mFormat).c_str());
- mAnalyticsItem->setCString(MM_PREFIX "source", toString(record->mAttributes.source).c_str());
- mAnalyticsItem->setInt32(MM_PREFIX "latency", (int32_t)record->mLatency); // bad estimate.
- mAnalyticsItem->setInt32(MM_PREFIX "samplerate", (int32_t)record->mSampleRate);
- mAnalyticsItem->setInt32(MM_PREFIX "channels", (int32_t)record->mChannelCount);
+ mMetricsItem->setCString(MM_PREFIX "encoding", toString(record->mFormat).c_str());
+ mMetricsItem->setCString(MM_PREFIX "source", toString(record->mAttributes.source).c_str());
+ mMetricsItem->setInt32(MM_PREFIX "latency", (int32_t)record->mLatency); // bad estimate.
+ mMetricsItem->setInt32(MM_PREFIX "samplerate", (int32_t)record->mSampleRate);
+ mMetricsItem->setInt32(MM_PREFIX "channels", (int32_t)record->mChannelCount);
// Non-API entries, these can change.
- mAnalyticsItem->setInt32(MM_PREFIX "portId", (int32_t)record->mPortId);
- mAnalyticsItem->setInt32(MM_PREFIX "frameCount", (int32_t)record->mFrameCount);
- mAnalyticsItem->setCString(MM_PREFIX "attributes", toString(record->mAttributes).c_str());
- mAnalyticsItem->setInt64(MM_PREFIX "channelMask", (int64_t)record->mChannelMask);
+ mMetricsItem->setInt32(MM_PREFIX "portId", (int32_t)record->mPortId);
+ mMetricsItem->setInt32(MM_PREFIX "frameCount", (int32_t)record->mFrameCount);
+ mMetricsItem->setCString(MM_PREFIX "attributes", toString(record->mAttributes).c_str());
+ mMetricsItem->setInt64(MM_PREFIX "channelMask", (int64_t)record->mChannelMask);
// log total duration recording, including anything currently running.
int64_t activeNs = 0;
if (mStartedNs != 0) {
activeNs = systemTime() - mStartedNs;
}
- mAnalyticsItem->setDouble(MM_PREFIX "durationMs", (mDurationNs + activeNs) * 1e-6);
- mAnalyticsItem->setInt64(MM_PREFIX "startCount", (int64_t)mCount);
+ mMetricsItem->setDouble(MM_PREFIX "durationMs", (mDurationNs + activeNs) * 1e-6);
+ mMetricsItem->setInt64(MM_PREFIX "startCount", (int64_t)mCount);
if (mLastError != NO_ERROR) {
- mAnalyticsItem->setInt32(MM_PREFIX "lastError.code", (int32_t)mLastError);
- mAnalyticsItem->setCString(MM_PREFIX "lastError.at", mLastErrorFunc.c_str());
+ mMetricsItem->setInt32(MM_PREFIX "lastError.code", (int32_t)mLastError);
+ mMetricsItem->setCString(MM_PREFIX "lastError.at", mLastErrorFunc.c_str());
}
}
+static const char *stateToString(bool active) {
+ return active ? "ACTIVE" : "STOPPED";
+}
+
// hand the user a snapshot of the metrics.
-status_t AudioRecord::getMetrics(MediaAnalyticsItem * &item)
+status_t AudioRecord::getMetrics(mediametrics::Item * &item)
{
mMediaMetrics.gather(this);
- MediaAnalyticsItem *tmp = mMediaMetrics.dup();
+ mediametrics::Item *tmp = mMediaMetrics.dup();
if (tmp == nullptr) {
return BAD_VALUE;
}
@@ -164,6 +168,11 @@
{
mMediaMetrics.gather(this);
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_DTOR)
+ .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)mStatus)
+ .record();
+
if (mStatus == NO_ERROR) {
// Make sure that callback function exits in the case where
// it is looping on buffer empty condition in obtainBuffer().
@@ -186,7 +195,7 @@
IPCThreadState::self()->flushCommands();
ALOGV("%s(%d): releasing session id %d",
__func__, mPortId, mSessionId);
- AudioSystem::releaseAudioSessionId(mSessionId, -1 /*pid*/);
+ AudioSystem::releaseAudioSessionId(mSessionId, mClientPid);
}
}
@@ -361,7 +370,7 @@
mMarkerReached = false;
mNewPosition = 0;
mUpdatePeriod = 0;
- AudioSystem::acquireAudioSessionId(mSessionId, -1);
+ AudioSystem::acquireAudioSessionId(mSessionId, mClientPid, mClientUid);
mSequence = 1;
mObservedSequence = mSequence;
mInOverrun = false;
@@ -380,11 +389,21 @@
status_t AudioRecord::start(AudioSystem::sync_event_t event, audio_session_t triggerSession)
{
+ const int64_t beginNs = systemTime();
ALOGV("%s(%d): sync event %d trigger session %d", __func__, mPortId, event, triggerSession);
-
AutoMutex lock(mLock);
+
+ status_t status = NO_ERROR;
+ mediametrics::Defer([&] {
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_START)
+ .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
+ .set(AMEDIAMETRICS_PROP_STATE, stateToString(mActive))
+ .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)status)
+ .record(); });
+
if (mActive) {
- return NO_ERROR;
+ return status;
}
// discard data in buffer
@@ -409,7 +428,6 @@
// mActive is checked by restoreRecord_l
mActive = true;
- status_t status = NO_ERROR;
if (!(flags & CBLK_INVALID)) {
status = mAudioRecord->start(event, triggerSession).transactionError();
if (status == DEAD_OBJECT) {
@@ -447,7 +465,15 @@
void AudioRecord::stop()
{
+ const int64_t beginNs = systemTime();
AutoMutex lock(mLock);
+ mediametrics::Defer([&] {
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_STOP)
+ .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
+ .set(AMEDIAMETRICS_PROP_STATE, stateToString(mActive))
+ .record(); });
+
ALOGV("%s(%d): mActive:%d\n", __func__, mPortId, mActive);
if (!mActive) {
return;
@@ -663,6 +689,7 @@
// must be called with mLock held
status_t AudioRecord::createRecord_l(const Modulo<uint32_t> &epoch, const String16& opPackageName)
{
+ const int64_t beginNs = systemTime();
const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
IAudioFlinger::CreateRecordInput input;
IAudioFlinger::CreateRecordOutput output;
@@ -849,6 +876,29 @@
mDeathNotifier = new DeathNotifier(this);
IInterface::asBinder(mAudioRecord)->linkToDeath(mDeathNotifier, this);
+ 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))
+ // 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_SESSIONID, (int32_t)mSessionId)
+ .set(AMEDIAMETRICS_PROP_TRACKID, mPortId)
+ .set(AMEDIAMETRICS_PROP_SOURCE, toString(mAttributes.source).c_str())
+ .set(AMEDIAMETRICS_PROP_THREADID, (int32_t)output.inputId)
+ .set(AMEDIAMETRICS_PROP_SELECTEDDEVICEID, (int32_t)mSelectedDeviceId)
+ .set(AMEDIAMETRICS_PROP_ROUTEDDEVICEID, (int32_t)mRoutedDeviceId)
+ .set(AMEDIAMETRICS_PROP_ENCODING, toString(mFormat).c_str())
+ .set(AMEDIAMETRICS_PROP_CHANNELMASK, (int32_t)mChannelMask)
+ .set(AMEDIAMETRICS_PROP_FRAMECOUNT, (int32_t)mFrameCount)
+ .set(AMEDIAMETRICS_PROP_SAMPLERATE, (int32_t)mSampleRate)
+ // the following are NOT immutable
+ .set(AMEDIAMETRICS_PROP_STATE, stateToString(mActive))
+ .set(AMEDIAMETRICS_PROP_SELECTEDMICDIRECTION, (int32_t)mSelectedMicDirection)
+ .set(AMEDIAMETRICS_PROP_SELECTEDMICFIELDDIRECTION, (double)mSelectedMicFieldDimension)
+ .record();
+
exit:
mStatus = status;
// sp<IAudioTrack> track destructor will cause releaseOutput() to be called by AudioFlinger
@@ -896,7 +946,6 @@
{
// previous and new IAudioRecord sequence numbers are used to detect track re-creation
uint32_t oldSequence = 0;
- uint32_t newSequence;
Proxy::Buffer buffer;
status_t status = NO_ERROR;
@@ -914,7 +963,7 @@
// start of lock scope
AutoMutex lock(mLock);
- newSequence = mSequence;
+ uint32_t newSequence = mSequence;
// did previous obtainBuffer() fail due to media server death or voluntary invalidation?
if (status == DEAD_OBJECT) {
// re-create track, unless someone else has already done so
@@ -951,6 +1000,7 @@
audioBuffer->frameCount = buffer.mFrameCount;
audioBuffer->size = buffer.mFrameCount * mFrameSize;
audioBuffer->raw = buffer.mRaw;
+ audioBuffer->sequence = oldSequence;
if (nonContig != NULL) {
*nonContig = buffer.mNonContig;
}
@@ -971,6 +1021,12 @@
buffer.mRaw = audioBuffer->raw;
AutoMutex lock(mLock);
+ if (audioBuffer->sequence != mSequence) {
+ // This Buffer came from a different IAudioRecord instance, so ignore the releaseBuffer
+ ALOGD("%s is no-op due to IAudioRecord sequence mismatch %u != %u",
+ __func__, audioBuffer->sequence, mSequence);
+ return;
+ }
mInOverrun = false;
mProxy->releaseBuffer(&buffer);
@@ -1282,6 +1338,17 @@
status_t AudioRecord::restoreRecord_l(const char *from)
{
+ status_t result = NO_ERROR; // logged: make sure to set this before returning.
+ const int64_t beginNs = systemTime();
+ mediametrics::Defer([&] {
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_RESTORE)
+ .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
+ .set(AMEDIAMETRICS_PROP_STATE, stateToString(mActive))
+ .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)result)
+ .set(AMEDIAMETRICS_PROP_WHERE, from)
+ .record(); });
+
ALOGW("%s(%d): dead IAudioRecord, creating a new one from %s()", __func__, mPortId, from);
++mSequence;
@@ -1300,7 +1367,7 @@
// It will also delete the strong references on previous IAudioRecord and IMemory
Modulo<uint32_t> position(mProxy->getPosition());
mNewPosition = position + mUpdatePeriod;
- status_t result = createRecord_l(position, mOpPackageName);
+ result = createRecord_l(position, mOpPackageName);
if (result == NO_ERROR) {
if (mActive) {
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index ad39abe..1769062 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -36,10 +36,11 @@
// client singleton for AudioFlinger binder interface
Mutex AudioSystem::gLock;
+Mutex AudioSystem::gLockErrorCallbacks;
Mutex AudioSystem::gLockAPS;
sp<IAudioFlinger> AudioSystem::gAudioFlinger;
sp<AudioSystem::AudioFlingerClient> AudioSystem::gAudioFlingerClient;
-audio_error_callback AudioSystem::gAudioErrorCallback = NULL;
+std::set<audio_error_callback> AudioSystem::gAudioErrorCallbacks;
dynamic_policy_callback AudioSystem::gDynPolicyCallback = NULL;
record_config_callback AudioSystem::gRecordConfigCallback = NULL;
@@ -63,9 +64,7 @@
if (gAudioFlingerClient == NULL) {
gAudioFlingerClient = new AudioFlingerClient();
} else {
- if (gAudioErrorCallback) {
- gAudioErrorCallback(NO_ERROR);
- }
+ reportError(NO_ERROR);
}
binder->linkToDeath(gAudioFlingerClient);
gAudioFlinger = interface_cast<IAudioFlinger>(binder);
@@ -434,11 +433,11 @@
return af->newAudioUniqueId(use);
}
-void AudioSystem::acquireAudioSessionId(audio_session_t audioSession, pid_t pid)
+void AudioSystem::acquireAudioSessionId(audio_session_t audioSession, pid_t pid, uid_t uid)
{
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af != 0) {
- af->acquireAudioSessionId(audioSession, pid);
+ af->acquireAudioSessionId(audioSession, pid, uid);
}
}
@@ -500,19 +499,16 @@
void AudioSystem::AudioFlingerClient::binderDied(const wp<IBinder>& who __unused)
{
- audio_error_callback cb = NULL;
{
Mutex::Autolock _l(AudioSystem::gLock);
AudioSystem::gAudioFlinger.clear();
- cb = gAudioErrorCallback;
}
// clear output handles and stream to output map caches
clearIoCache();
- if (cb) {
- cb(DEAD_OBJECT);
- }
+ reportError(DEAD_OBJECT);
+
ALOGW("AudioFlinger server died!");
}
@@ -717,10 +713,23 @@
return NO_ERROR;
}
-/* static */ void AudioSystem::setErrorCallback(audio_error_callback cb)
+/* static */ uintptr_t AudioSystem::addErrorCallback(audio_error_callback cb)
{
- Mutex::Autolock _l(gLock);
- gAudioErrorCallback = cb;
+ Mutex::Autolock _l(gLockErrorCallbacks);
+ gAudioErrorCallbacks.insert(cb);
+ return reinterpret_cast<uintptr_t>(cb);
+}
+
+/* static */ void AudioSystem::removeErrorCallback(uintptr_t cb) {
+ Mutex::Autolock _l(gLockErrorCallbacks);
+ gAudioErrorCallbacks.erase(reinterpret_cast<audio_error_callback>(cb));
+}
+
+/* static */ void AudioSystem::reportError(status_t err) {
+ Mutex::Autolock _l(gLockErrorCallbacks);
+ for (auto callback : gAudioErrorCallbacks) {
+ callback(err);
+ }
}
/*static*/ void AudioSystem::setDynPolicyCallback(dynamic_policy_callback cb)
@@ -1017,6 +1026,16 @@
return aps->getDevicesForStream(stream);
}
+status_t AudioSystem::getDevicesForAttributes(const AudioAttributes &aa,
+ AudioDeviceTypeAddrVector *devices) {
+ if (devices == nullptr) {
+ return BAD_VALUE;
+ }
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return PERMISSION_DENIED;
+ return aps->getDevicesForAttributes(aa, devices);
+}
+
audio_io_handle_t AudioSystem::getOutputForEffect(const effect_descriptor_t *desc)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
@@ -1123,6 +1142,12 @@
}
}
+status_t AudioSystem::setSupportedSystemUsages(const std::vector<audio_usage_t>& systemUsages) {
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == nullptr) return PERMISSION_DENIED;
+ return aps->setSupportedSystemUsages(systemUsages);
+}
+
status_t AudioSystem::setAllowedCapturePolicy(uid_t uid, audio_flags_mask_t flags) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == nullptr) return PERMISSION_DENIED;
@@ -1334,6 +1359,21 @@
return aps->removeUidDeviceAffinities(uid);
}
+status_t AudioSystem::setUserIdDeviceAffinities(int userId,
+ const Vector<AudioDeviceTypeAddr>& devices)
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return PERMISSION_DENIED;
+ return aps->setUserIdDeviceAffinities(userId, devices);
+}
+
+status_t AudioSystem::removeUserIdDeviceAffinities(int userId)
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return PERMISSION_DENIED;
+ return aps->removeUserIdDeviceAffinities(userId);
+}
+
status_t AudioSystem::startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,
audio_port_handle_t *portId)
@@ -1491,7 +1531,14 @@
}
}
}
- ALOGE("invalid attributes %s when converting to stream", toString(attr).c_str());
+ switch (attr.usage) {
+ case AUDIO_USAGE_VIRTUAL_SOURCE:
+ // virtual source is not expected to have an associated product strategy
+ break;
+ default:
+ ALOGE("invalid attributes %s when converting to stream", toString(attr).c_str());
+ break;
+ }
return AUDIO_STREAM_MUSIC;
}
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index e8d7b60..d5690fb 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -35,7 +35,7 @@
#include <media/AudioParameter.h>
#include <media/AudioResamplerPublic.h>
#include <media/AudioSystem.h>
-#include <media/MediaAnalyticsItem.h>
+#include <media/MediaMetricsItem.h>
#include <media/TypeConverter.h>
#define WAIT_PERIOD_MS 10
@@ -183,26 +183,26 @@
#define MM_PREFIX "android.media.audiotrack." // avoid cut-n-paste errors.
// Java API 28 entries, do not change.
- mAnalyticsItem->setCString(MM_PREFIX "streamtype", toString(track->streamType()).c_str());
- mAnalyticsItem->setCString(MM_PREFIX "type",
+ mMetricsItem->setCString(MM_PREFIX "streamtype", toString(track->streamType()).c_str());
+ mMetricsItem->setCString(MM_PREFIX "type",
toString(track->mAttributes.content_type).c_str());
- mAnalyticsItem->setCString(MM_PREFIX "usage", toString(track->mAttributes.usage).c_str());
+ mMetricsItem->setCString(MM_PREFIX "usage", toString(track->mAttributes.usage).c_str());
// Non-API entries, these can change due to a Java string mistake.
- mAnalyticsItem->setInt32(MM_PREFIX "sampleRate", (int32_t)track->mSampleRate);
- mAnalyticsItem->setInt64(MM_PREFIX "channelMask", (int64_t)track->mChannelMask);
+ mMetricsItem->setInt32(MM_PREFIX "sampleRate", (int32_t)track->mSampleRate);
+ mMetricsItem->setInt64(MM_PREFIX "channelMask", (int64_t)track->mChannelMask);
// Non-API entries, these can change.
- mAnalyticsItem->setInt32(MM_PREFIX "portId", (int32_t)track->mPortId);
- mAnalyticsItem->setCString(MM_PREFIX "encoding", toString(track->mFormat).c_str());
- mAnalyticsItem->setInt32(MM_PREFIX "frameCount", (int32_t)track->mFrameCount);
- mAnalyticsItem->setCString(MM_PREFIX "attributes", toString(track->mAttributes).c_str());
+ mMetricsItem->setInt32(MM_PREFIX "portId", (int32_t)track->mPortId);
+ mMetricsItem->setCString(MM_PREFIX "encoding", toString(track->mFormat).c_str());
+ mMetricsItem->setInt32(MM_PREFIX "frameCount", (int32_t)track->mFrameCount);
+ mMetricsItem->setCString(MM_PREFIX "attributes", toString(track->mAttributes).c_str());
}
// hand the user a snapshot of the metrics.
-status_t AudioTrack::getMetrics(MediaAnalyticsItem * &item)
+status_t AudioTrack::getMetrics(mediametrics::Item * &item)
{
mMediaMetrics.gather(this);
- MediaAnalyticsItem *tmp = mMediaMetrics.dup();
+ mediametrics::Item *tmp = mMediaMetrics.dup();
if (tmp == nullptr) {
return BAD_VALUE;
}
@@ -296,6 +296,12 @@
// pull together the numbers, before we clean up our structures
mMediaMetrics.gather(this);
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_DTOR)
+ .set(AMEDIAMETRICS_PROP_STATE, stateToString(mState))
+ .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)mStatus)
+ .record();
+
if (mStatus == NO_ERROR) {
// Make sure that callback function exits in the case where
// it is looping on buffer full condition in obtainBuffer().
@@ -599,7 +605,7 @@
mReleased = 0;
mStartNs = 0;
mStartFromZeroUs = 0;
- AudioSystem::acquireAudioSessionId(mSessionId, mClientPid);
+ AudioSystem::acquireAudioSessionId(mSessionId, mClientPid, mClientUid);
mSequence = 1;
mObservedSequence = mSequence;
mInUnderrun = false;
@@ -626,11 +632,23 @@
status_t AudioTrack::start()
{
+ const int64_t beginNs = systemTime();
AutoMutex lock(mLock);
+
+ status_t status = NO_ERROR; // logged: make sure to set this before returning.
+ mediametrics::Defer([&] {
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_START)
+ .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
+ .set(AMEDIAMETRICS_PROP_STATE, stateToString(mState))
+ .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)status)
+ .record(); });
+
ALOGV("%s(%d): prior state:%s", __func__, mPortId, stateToString(mState));
if (mState == STATE_ACTIVE) {
- return INVALID_OPERATION;
+ status = INVALID_OPERATION;
+ return status;
}
mInUnderrun = true;
@@ -703,7 +721,6 @@
mNewPosition = mPosition + mUpdatePeriod;
int32_t flags = android_atomic_and(~(CBLK_STREAM_END_DONE | CBLK_DISABLED), &mCblk->mFlags);
- status_t status = NO_ERROR;
if (!(flags & CBLK_INVALID)) {
status = mAudioTrack->start();
if (status == DEAD_OBJECT) {
@@ -749,7 +766,16 @@
void AudioTrack::stop()
{
+ const int64_t beginNs = systemTime();
+
AutoMutex lock(mLock);
+ mediametrics::Defer([&]() {
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_STOP)
+ .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
+ .set(AMEDIAMETRICS_PROP_STATE, stateToString(mState))
+ .record(); });
+
ALOGV("%s(%d): prior state:%s", __func__, mPortId, stateToString(mState));
if (mState != STATE_ACTIVE && mState != STATE_PAUSED) {
@@ -801,7 +827,15 @@
void AudioTrack::flush()
{
+ const int64_t beginNs = systemTime();
AutoMutex lock(mLock);
+ mediametrics::Defer([&]() {
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_FLUSH)
+ .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
+ .set(AMEDIAMETRICS_PROP_STATE, stateToString(mState))
+ .record(); });
+
ALOGV("%s(%d): prior state:%s", __func__, mPortId, stateToString(mState));
if (mSharedBuffer != 0) {
@@ -834,7 +868,15 @@
void AudioTrack::pause()
{
+ const int64_t beginNs = systemTime();
AutoMutex lock(mLock);
+ mediametrics::Defer([&]() {
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_PAUSE)
+ .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
+ .set(AMEDIAMETRICS_PROP_STATE, stateToString(mState))
+ .record(); });
+
ALOGV("%s(%d): prior state:%s", __func__, mPortId, stateToString(mState));
if (mState == STATE_ACTIVE) {
@@ -876,6 +918,12 @@
return BAD_VALUE;
}
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_SETVOLUME)
+ .set(AMEDIAMETRICS_PROP_VOLUME_LEFT, (double)left)
+ .set(AMEDIAMETRICS_PROP_VOLUME_RIGHT, (double)right)
+ .record();
+
AutoMutex lock(mLock);
mVolume[AUDIO_INTERLEAVE_LEFT] = left;
mVolume[AUDIO_INTERLEAVE_RIGHT] = right;
@@ -1027,6 +1075,20 @@
//set effective rates
mProxy->setPlaybackRate(playbackRateTemp);
mProxy->setSampleRate(effectiveRate); // FIXME: not quite "atomic" with setPlaybackRate
+
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_SETPLAYBACKPARAM)
+ .set(AMEDIAMETRICS_PROP_SAMPLERATE, (int32_t)mSampleRate)
+ .set(AMEDIAMETRICS_PROP_PLAYBACK_SPEED, (double)mPlaybackRate.mSpeed)
+ .set(AMEDIAMETRICS_PROP_PLAYBACK_PITCH, (double)mPlaybackRate.mPitch)
+ .set(AMEDIAMETRICS_PROP_PREFIX_EFFECTIVE
+ AMEDIAMETRICS_PROP_SAMPLERATE, (int32_t)effectiveRate)
+ .set(AMEDIAMETRICS_PROP_PREFIX_EFFECTIVE
+ AMEDIAMETRICS_PROP_PLAYBACK_SPEED, (double)playbackRateTemp.mSpeed)
+ .set(AMEDIAMETRICS_PROP_PREFIX_EFFECTIVE
+ AMEDIAMETRICS_PROP_PLAYBACK_PITCH, (double)playbackRateTemp.mPitch)
+ .record();
+
return NO_ERROR;
}
@@ -1618,6 +1680,48 @@
mDeathNotifier = new DeathNotifier(this);
IInterface::asBinder(mAudioTrack)->linkToDeath(mDeathNotifier, this);
+ // This is the first log sent from the AudioTrack client.
+ // 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.
+ 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_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)
+ .set(AMEDIAMETRICS_PROP_SELECTEDDEVICEID, (int32_t)mSelectedDeviceId)
+ .set(AMEDIAMETRICS_PROP_ROUTEDDEVICEID, (int32_t)mRoutedDeviceId)
+ .set(AMEDIAMETRICS_PROP_ENCODING, toString(mFormat).c_str())
+ .set(AMEDIAMETRICS_PROP_CHANNELMASK, (int32_t)mChannelMask)
+ .set(AMEDIAMETRICS_PROP_FRAMECOUNT, (int32_t)mFrameCount)
+ // the following are NOT immutable
+ .set(AMEDIAMETRICS_PROP_VOLUME_LEFT, (double)mVolume[AUDIO_INTERLEAVE_LEFT])
+ .set(AMEDIAMETRICS_PROP_VOLUME_RIGHT, (double)mVolume[AUDIO_INTERLEAVE_RIGHT])
+ .set(AMEDIAMETRICS_PROP_STATE, stateToString(mState))
+ .set(AMEDIAMETRICS_PROP_AUXEFFECTID, (int32_t)mAuxEffectId)
+ .set(AMEDIAMETRICS_PROP_SAMPLERATE, (int32_t)mSampleRate)
+ .set(AMEDIAMETRICS_PROP_PLAYBACK_SPEED, (double)mPlaybackRate.mSpeed)
+ .set(AMEDIAMETRICS_PROP_PLAYBACK_PITCH, (double)mPlaybackRate.mPitch)
+ .set(AMEDIAMETRICS_PROP_PREFIX_EFFECTIVE
+ AMEDIAMETRICS_PROP_SAMPLERATE, (int32_t)effectiveSampleRate)
+ .set(AMEDIAMETRICS_PROP_PREFIX_EFFECTIVE
+ AMEDIAMETRICS_PROP_PLAYBACK_SPEED, (double)effectiveSpeed)
+ .set(AMEDIAMETRICS_PROP_PREFIX_EFFECTIVE
+ AMEDIAMETRICS_PROP_PLAYBACK_PITCH, (double)effectivePitch)
+ .record();
+
+ // mSendLevel
+ // mReqFrameCount?
+ // mNotificationFramesAct, mNotificationFramesReq, mNotificationsPerBufferReq
+ // mLatency, mAfLatency, mAfFrameCount, mAfSampleRate
+
}
exit:
@@ -1673,7 +1777,6 @@
{
// previous and new IAudioTrack sequence numbers are used to detect track re-creation
uint32_t oldSequence = 0;
- uint32_t newSequence;
Proxy::Buffer buffer;
status_t status = NO_ERROR;
@@ -1690,7 +1793,7 @@
{ // start of lock scope
AutoMutex lock(mLock);
- newSequence = mSequence;
+ uint32_t newSequence = mSequence;
// did previous obtainBuffer() fail due to media server death or voluntary invalidation?
if (status == DEAD_OBJECT) {
// re-create track, unless someone else has already done so
@@ -1737,6 +1840,7 @@
audioBuffer->frameCount = buffer.mFrameCount;
audioBuffer->size = buffer.mFrameCount * mFrameSize;
audioBuffer->raw = buffer.mRaw;
+ audioBuffer->sequence = oldSequence;
if (nonContig != NULL) {
*nonContig = buffer.mNonContig;
}
@@ -1760,6 +1864,12 @@
buffer.mRaw = audioBuffer->raw;
AutoMutex lock(mLock);
+ if (audioBuffer->sequence != mSequence) {
+ // This Buffer came from a different IAudioTrack instance, so ignore the releaseBuffer
+ ALOGD("%s is no-op due to IAudioTrack sequence mismatch %u != %u",
+ __func__, audioBuffer->sequence, mSequence);
+ return;
+ }
mReleased += stepCount;
mInUnderrun = false;
mProxy->releaseBuffer(&buffer);
@@ -2294,6 +2404,17 @@
status_t AudioTrack::restoreTrack_l(const char *from)
{
+ status_t result = NO_ERROR; // logged: make sure to set this before returning.
+ const int64_t beginNs = systemTime();
+ mediametrics::Defer([&] {
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_RESTORE)
+ .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
+ .set(AMEDIAMETRICS_PROP_STATE, stateToString(mState))
+ .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)result)
+ .set(AMEDIAMETRICS_PROP_WHERE, from)
+ .record(); });
+
ALOGW("%s(%d): dead IAudioTrack, %s, creating a new one from %s()",
__func__, mPortId, isOffloadedOrDirect_l() ? "Offloaded or Direct" : "PCM", from);
++mSequence;
@@ -2305,7 +2426,8 @@
if (isOffloadedOrDirect_l() || mDoNotReconnect) {
// FIXME re-creation of offloaded and direct tracks is not yet implemented;
// reconsider enabling for linear PCM encodings when position can be preserved.
- return DEAD_OBJECT;
+ result = DEAD_OBJECT;
+ return result;
}
// Save so we can return count since creation.
@@ -2336,7 +2458,7 @@
// following member variables: mAudioTrack, mCblkMemory and mCblk.
// It will also delete the strong references on previous IAudioTrack and IMemory.
// If a new IAudioTrack cannot be created, the previous (dead) instance will be left intact.
- status_t result = createTrack_l();
+ result = createTrack_l();
if (result == NO_ERROR) {
// take the frames that will be lost by track recreation into account in saved position
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index cbc98bd..513da2b 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -571,12 +571,13 @@
return id;
}
- virtual void acquireAudioSessionId(audio_session_t audioSession, int pid)
+ void acquireAudioSessionId(audio_session_t audioSession, pid_t pid, uid_t uid) override
{
Parcel data, reply;
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
data.writeInt32(audioSession);
- data.writeInt32(pid);
+ data.writeInt32((int32_t)pid);
+ data.writeInt32((int32_t)uid);
remote()->transact(ACQUIRE_AUDIO_SESSION_ID, data, &reply);
}
@@ -1326,8 +1327,9 @@
case ACQUIRE_AUDIO_SESSION_ID: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
audio_session_t audioSession = (audio_session_t) data.readInt32();
- int pid = data.readInt32();
- acquireAudioSessionId(audioSession, pid);
+ const pid_t pid = (pid_t)data.readInt32();
+ const uid_t uid = (uid_t)data.readInt32();
+ acquireAudioSessionId(audioSession, pid, uid);
return NO_ERROR;
} break;
case RELEASE_AUDIO_SESSION_ID: {
diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp
index ab942df..cc78ec1 100644
--- a/media/libaudioclient/IAudioPolicyService.cpp
+++ b/media/libaudioclient/IAudioPolicyService.cpp
@@ -97,11 +97,14 @@
IS_HAPTIC_PLAYBACK_SUPPORTED,
SET_UID_DEVICE_AFFINITY,
REMOVE_UID_DEVICE_AFFINITY,
+ SET_USERID_DEVICE_AFFINITY,
+ REMOVE_USERID_DEVICE_AFFINITY,
GET_OFFLOAD_FORMATS_A2DP,
LIST_AUDIO_PRODUCT_STRATEGIES,
GET_STRATEGY_FOR_ATTRIBUTES,
LIST_AUDIO_VOLUME_GROUPS,
GET_VOLUME_GROUP_FOR_ATTRIBUTES,
+ SET_SUPPORTED_SYSTEM_USAGES,
SET_ALLOWED_CAPTURE_POLICY,
MOVE_EFFECTS_TO_IO,
SET_RTT_ENABLED,
@@ -109,6 +112,7 @@
SET_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY,
REMOVE_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY,
GET_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY,
+ GET_DEVICES_FOR_ATTRIBUTES,
};
#define MAX_ITEMS_PER_LIST 1024
@@ -331,6 +335,7 @@
ALOGE("getInputForAttr NULL portId - shouldn't happen");
return BAD_VALUE;
}
+
data.write(attr, sizeof(audio_attributes_t));
data.writeInt32(*input);
data.writeInt32(riid);
@@ -621,6 +626,20 @@
return status;
}
+ status_t setSupportedSystemUsages(const std::vector<audio_usage_t>& systemUsages) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ data.writeInt32(systemUsages.size());
+ for (auto systemUsage : systemUsages) {
+ data.writeInt32(systemUsage);
+ }
+ status_t status = remote()->transact(SET_SUPPORTED_SYSTEM_USAGES, data, &reply);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ return static_cast <status_t> (reply.readInt32());
+ }
+
status_t setAllowedCapturePolicy(uid_t uid, audio_flags_mask_t flags) override {
Parcel data, reply;
data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
@@ -1180,6 +1199,52 @@
return status;
}
+ virtual status_t setUserIdDeviceAffinities(int userId,
+ const Vector<AudioDeviceTypeAddr>& devices)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+
+ data.writeInt32((int32_t) userId);
+ size_t size = devices.size();
+ size_t sizePosition = data.dataPosition();
+ data.writeInt32((int32_t) size);
+ size_t finalSize = size;
+ for (size_t i = 0; i < size; i++) {
+ size_t position = data.dataPosition();
+ if (devices[i].writeToParcel(&data) != NO_ERROR) {
+ data.setDataPosition(position);
+ finalSize--;
+ }
+ }
+ if (size != finalSize) {
+ size_t position = data.dataPosition();
+ data.setDataPosition(sizePosition);
+ data.writeInt32(finalSize);
+ data.setDataPosition(position);
+ }
+
+ status_t status = remote()->transact(SET_USERID_DEVICE_AFFINITY, data, &reply);
+ if (status == NO_ERROR) {
+ status = (status_t)reply.readInt32();
+ }
+ return status;
+ }
+
+ virtual status_t removeUserIdDeviceAffinities(int userId) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+
+ data.writeInt32((int32_t) userId);
+
+ status_t status =
+ remote()->transact(REMOVE_USERID_DEVICE_AFFINITY, data, &reply);
+ if (status == NO_ERROR) {
+ status = (status_t) reply.readInt32();
+ }
+ return status;
+ }
+
virtual status_t listAudioProductStrategies(AudioProductStrategyVector &strategies)
{
Parcel data, reply;
@@ -1348,6 +1413,41 @@
}
return static_cast<status_t>(reply.readInt32());
}
+
+ virtual status_t getDevicesForAttributes(const AudioAttributes &aa,
+ AudioDeviceTypeAddrVector *devices) const
+ {
+ if (devices == nullptr) {
+ return BAD_VALUE;
+ }
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ status_t status = aa.writeToParcel(&data);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ status = remote()->transact(GET_DEVICES_FOR_ATTRIBUTES, data, &reply);
+ if (status != NO_ERROR) {
+ // transaction failed, return error
+ return status;
+ }
+ status = static_cast<status_t>(reply.readInt32());
+ if (status != NO_ERROR) {
+ // APM method call failed, return error
+ return status;
+ }
+
+ const size_t numberOfDevices = (size_t)reply.readInt32();
+ for (size_t i = 0; i < numberOfDevices; i++) {
+ AudioDeviceTypeAddr device;
+ if (device.readFromParcel((Parcel*)&reply) == NO_ERROR) {
+ devices->push_back(device);
+ } else {
+ return FAILED_TRANSACTION;
+ }
+ }
+ return NO_ERROR;
+ }
};
IMPLEMENT_META_INTERFACE(AudioPolicyService, "android.media.IAudioPolicyService");
@@ -1371,8 +1471,6 @@
case UNREGISTER_EFFECT:
case SET_EFFECT_ENABLED:
case GET_OUTPUT_FOR_ATTR:
- case ACQUIRE_SOUNDTRIGGER_SESSION:
- case RELEASE_SOUNDTRIGGER_SESSION:
case MOVE_EFFECTS_TO_IO:
ALOGW("%s: transaction %d received from PID %d",
__func__, code, IPCThreadState::self()->getCallingPid());
@@ -1407,14 +1505,21 @@
case SET_A11Y_SERVICES_UIDS:
case SET_UID_DEVICE_AFFINITY:
case REMOVE_UID_DEVICE_AFFINITY:
+ case SET_USERID_DEVICE_AFFINITY:
+ case REMOVE_USERID_DEVICE_AFFINITY:
case GET_OFFLOAD_FORMATS_A2DP:
case LIST_AUDIO_VOLUME_GROUPS:
case GET_VOLUME_GROUP_FOR_ATTRIBUTES:
+ case ACQUIRE_SOUNDTRIGGER_SESSION:
+ case RELEASE_SOUNDTRIGGER_SESSION:
case SET_RTT_ENABLED:
case IS_CALL_SCREEN_MODE_SUPPORTED:
case SET_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY:
+ case SET_SUPPORTED_SYSTEM_USAGES:
case REMOVE_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY:
- case GET_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY: {
+ case GET_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY:
+ case GET_DEVICES_FOR_ATTRIBUTES:
+ case SET_ALLOWED_CAPTURE_POLICY: {
if (!isServiceUid(IPCThreadState::self()->getCallingUid())) {
ALOGW("%s: transaction %d received from PID %d unauthorized UID %d",
__func__, code, IPCThreadState::self()->getCallingPid(),
@@ -2027,8 +2132,6 @@
case ACQUIRE_SOUNDTRIGGER_SESSION: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
- sp<IAudioPolicyServiceClient> client = interface_cast<IAudioPolicyServiceClient>(
- data.readStrongBinder());
audio_session_t session = AUDIO_SESSION_NONE;
audio_io_handle_t ioHandle = AUDIO_IO_HANDLE_NONE;
audio_devices_t device = AUDIO_DEVICE_NONE;
@@ -2044,8 +2147,6 @@
case RELEASE_SOUNDTRIGGER_SESSION: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
- sp<IAudioPolicyServiceClient> client = interface_cast<IAudioPolicyServiceClient>(
- data.readStrongBinder());
audio_session_t session = (audio_session_t)data.readInt32();
status_t status = releaseSoundTriggerSession(session);
reply->writeInt32(status);
@@ -2329,6 +2430,30 @@
return NO_ERROR;
}
+ case SET_USERID_DEVICE_AFFINITY: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ const int userId = (int) data.readInt32();
+ Vector<AudioDeviceTypeAddr> devices;
+ size_t size = (size_t)data.readInt32();
+ for (size_t i = 0; i < size; i++) {
+ AudioDeviceTypeAddr device;
+ if (device.readFromParcel((Parcel*)&data) == NO_ERROR) {
+ devices.add(device);
+ }
+ }
+ status_t status = setUserIdDeviceAffinities(userId, devices);
+ reply->writeInt32(status);
+ return NO_ERROR;
+ }
+
+ case REMOVE_USERID_DEVICE_AFFINITY: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ const int userId = (int) data.readInt32();
+ status_t status = removeUserIdDeviceAffinities(userId);
+ reply->writeInt32(status);
+ return NO_ERROR;
+ }
+
case LIST_AUDIO_PRODUCT_STRATEGIES: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
AudioProductStrategyVector strategies;
@@ -2409,16 +2534,46 @@
if (status != NO_ERROR) {
return status;
}
+
volume_group_t group;
status = getVolumeGroupFromAudioAttributes(attributes, group);
- reply->writeInt32(status);
if (status != NO_ERROR) {
return NO_ERROR;
}
+
+ reply->writeInt32(status);
reply->writeUint32(static_cast<int>(group));
return NO_ERROR;
}
+ case SET_SUPPORTED_SYSTEM_USAGES: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ std::vector<audio_usage_t> systemUsages;
+
+ int32_t size;
+ status_t status = data.readInt32(&size);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ if (size > MAX_ITEMS_PER_LIST) {
+ size = MAX_ITEMS_PER_LIST;
+ }
+
+ for (int32_t i = 0; i < size; i++) {
+ int32_t systemUsageInt;
+ status = data.readInt32(&systemUsageInt);
+ if (status != NO_ERROR) {
+ return status;
+ }
+
+ audio_usage_t systemUsage = static_cast<audio_usage_t>(systemUsageInt);
+ systemUsages.push_back(systemUsage);
+ }
+ status = setSupportedSystemUsages(systemUsages);
+ reply->writeInt32(static_cast <int32_t>(status));
+ return NO_ERROR;
+ }
+
case SET_ALLOWED_CAPTURE_POLICY: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
uid_t uid = data.readInt32();
@@ -2477,6 +2632,37 @@
return NO_ERROR;
}
+ case GET_DEVICES_FOR_ATTRIBUTES: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ AudioAttributes attributes;
+ status_t status = attributes.readFromParcel(&data);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ AudioDeviceTypeAddrVector devices;
+ status = getDevicesForAttributes(attributes.getAttributes(), &devices);
+ // reply data formatted as:
+ // - (int32) method call result from APM
+ // - (int32) number of devices (n) if method call returned NO_ERROR
+ // - n AudioDeviceTypeAddr if method call returned NO_ERROR
+ reply->writeInt32(status);
+ if (status != NO_ERROR) {
+ return NO_ERROR;
+ }
+ status = reply->writeInt32(devices.size());
+ if (status != NO_ERROR) {
+ return status;
+ }
+ for (const auto& device : devices) {
+ status = device.writeToParcel(reply);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+
+ return NO_ERROR;
+ }
+
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libaudioclient/include/media/AudioEffect.h b/media/libaudioclient/include/media/AudioEffect.h
index f17d737..eec9dfc 100644
--- a/media/libaudioclient/include/media/AudioEffect.h
+++ b/media/libaudioclient/include/media/AudioEffect.h
@@ -639,7 +639,8 @@
sp<EffectClient> mIEffectClient; // IEffectClient implementation
sp<IMemory> mCblkMemory; // shared memory for deferred parameter setting
effect_param_cblk_t* mCblk; // control block for deferred parameter setting
- pid_t mClientPid;
+ pid_t mClientPid = (pid_t)-1;
+ uid_t mClientUid = (uid_t)-1;
};
diff --git a/media/libaudioclient/include/media/AudioPolicy.h b/media/libaudioclient/include/media/AudioPolicy.h
index 0ab1c9d..20d2c0b 100644
--- a/media/libaudioclient/include/media/AudioPolicy.h
+++ b/media/libaudioclient/include/media/AudioPolicy.h
@@ -18,12 +18,14 @@
#ifndef ANDROID_AUDIO_POLICY_H
#define ANDROID_AUDIO_POLICY_H
+#include <functional>
#include <binder/Parcel.h>
#include <media/AudioDeviceTypeAddr.h>
#include <system/audio.h>
#include <system/audio_policy.h>
#include <utils/String8.h>
#include <utils/Vector.h>
+#include <cutils/multiuser.h>
namespace android {
@@ -32,10 +34,12 @@
#define RULE_MATCH_ATTRIBUTE_USAGE 0x1
#define RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET (0x1 << 1)
#define RULE_MATCH_UID (0x1 << 2)
+#define RULE_MATCH_USERID (0x1 << 3)
#define RULE_EXCLUDE_ATTRIBUTE_USAGE (RULE_EXCLUSION_MASK|RULE_MATCH_ATTRIBUTE_USAGE)
#define RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET \
(RULE_EXCLUSION_MASK|RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET)
#define RULE_EXCLUDE_UID (RULE_EXCLUSION_MASK|RULE_MATCH_UID)
+#define RULE_EXCLUDE_USERID (RULE_EXCLUSION_MASK|RULE_MATCH_USERID)
#define MIX_TYPE_INVALID (-1)
#define MIX_TYPE_PLAYERS 0
@@ -73,6 +77,7 @@
audio_usage_t mUsage;
audio_source_t mSource;
uid_t mUid;
+ int mUserId;
} mValue;
uint32_t mRule;
};
@@ -98,9 +103,23 @@
bool hasUidRule(bool match, uid_t uid) const;
/** returns true if this mix has a rule for uid match (any uid) */
bool hasMatchUidRule() const;
+
+ void setExcludeUserId(int userId) const;
+ void setMatchUserId(int userId) const;
+ /** returns true if this mix has a rule to match or exclude the given userId */
+ bool hasUserIdRule(bool match, int userId) const;
+ /** returns true if this mix has a rule for userId match (any userId) */
+ bool hasMatchUserIdRule() const;
/** returns true if this mix can be used for uid-device affinity routing */
bool isDeviceAffinityCompatible() const;
+ /**
+ * returns true if the mix has a capture rule for a usage that
+ * matches the given predicate
+ */
+ bool hasMatchingRuleForUsage(
+ std::function<bool (audio_usage_t)>const& func) const;
+
mutable Vector<AudioMixMatchCriterion> mCriteria;
uint32_t mMixType;
audio_config_t mFormat;
diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h
index a3c0fe4..5c300ed 100644
--- a/media/libaudioclient/include/media/AudioRecord.h
+++ b/media/libaudioclient/include/media/AudioRecord.h
@@ -24,7 +24,7 @@
#include <cutils/sched_policy.h>
#include <media/AudioSystem.h>
#include <media/AudioTimestamp.h>
-#include <media/MediaAnalyticsItem.h>
+#include <media/MediaMetricsItem.h>
#include <media/Modulo.h>
#include <media/MicrophoneInfo.h>
#include <media/RecordingActivityTracker.h>
@@ -92,6 +92,11 @@
int8_t* i8; // unsigned 8-bit, offset by 0x80
// input to obtainBuffer(): unused, output: pointer to buffer
};
+
+ uint32_t sequence; // IAudioRecord instance sequence number, as of obtainBuffer().
+ // It is set by obtainBuffer() and confirmed by releaseBuffer().
+ // Not "user-serviceable".
+ // TODO Consider sp<IMemory> instead, or in addition to this.
};
/* As a convenience, if a callback is supplied, a handler thread
@@ -270,7 +275,7 @@
/*
* return metrics information for the current instance.
*/
- status_t getMetrics(MediaAnalyticsItem * &item);
+ status_t getMetrics(mediametrics::Item * &item);
/* After it's created the track is not active. Call start() to
* make it active. If set, the callback will start being called.
@@ -420,14 +425,17 @@
* frameCount number of frames requested
* size ignored
* raw ignored
+ * sequence ignored
* After error return:
* frameCount 0
* size 0
* raw undefined
+ * sequence undefined
* After successful return:
* frameCount actual number of frames available, <= number requested
* size actual number of bytes available
* raw pointer to the buffer
+ * sequence IAudioRecord instance sequence number, as of obtainBuffer()
*/
status_t obtainBuffer(Buffer* audioBuffer, int32_t waitCount,
@@ -733,27 +741,27 @@
private:
class MediaMetrics {
public:
- MediaMetrics() : mAnalyticsItem(MediaAnalyticsItem::create("audiorecord")),
+ MediaMetrics() : mMetricsItem(mediametrics::Item::create("audiorecord")),
mCreatedNs(systemTime(SYSTEM_TIME_REALTIME)),
mStartedNs(0), mDurationNs(0), mCount(0),
mLastError(NO_ERROR) {
}
~MediaMetrics() {
- // mAnalyticsItem alloc failure will be flagged in the constructor
+ // mMetricsItem alloc failure will be flagged in the constructor
// don't log empty records
- if (mAnalyticsItem->count() > 0) {
- mAnalyticsItem->selfrecord();
+ if (mMetricsItem->count() > 0) {
+ mMetricsItem->selfrecord();
}
}
void gather(const AudioRecord *record);
- MediaAnalyticsItem *dup() { return mAnalyticsItem->dup(); }
+ mediametrics::Item *dup() { return mMetricsItem->dup(); }
void logStart(nsecs_t when) { mStartedNs = when; mCount++; }
void logStop(nsecs_t when) { mDurationNs += (when-mStartedNs); mStartedNs = 0;}
void markError(status_t errcode, const char *func)
{ mLastError = errcode; mLastErrorFunc = func;}
private:
- std::unique_ptr<MediaAnalyticsItem> mAnalyticsItem;
+ std::unique_ptr<mediametrics::Item> mMetricsItem;
nsecs_t mCreatedNs; // XXX: perhaps not worth it in production
nsecs_t mStartedNs;
nsecs_t mDurationNs;
@@ -763,6 +771,7 @@
std::string mLastErrorFunc;
};
MediaMetrics mMediaMetrics;
+ std::string mMetricsId; // GUARDED_BY(mLock), could change in createRecord_l().
};
}; // namespace android
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index c4b528e..9d3f8b6 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -27,6 +27,7 @@
#include <media/IAudioFlingerClient.h>
#include <media/IAudioPolicyServiceClient.h>
#include <media/MicrophoneInfo.h>
+#include <set>
#include <system/audio.h>
#include <system/audio_effect.h>
#include <system/audio_policy.h>
@@ -107,7 +108,16 @@
static status_t setParameters(const String8& keyValuePairs);
static String8 getParameters(const String8& keys);
- static void setErrorCallback(audio_error_callback cb);
+ // Registers an error callback. When this callback is invoked, it means all
+ // state implied by this interface has been reset.
+ // Returns a token that can be used for un-registering.
+ // Might block while callbacks are being invoked.
+ static uintptr_t addErrorCallback(audio_error_callback cb);
+
+ // Un-registers a callback previously added with addErrorCallback.
+ // Might block while callbacks are being invoked.
+ static void removeErrorCallback(uintptr_t cb);
+
static void setDynPolicyCallback(dynamic_policy_callback cb);
static void setRecordConfigCallback(record_config_callback);
@@ -172,7 +182,7 @@
// or an unspecified existing unique ID.
static audio_unique_id_t newAudioUniqueId(audio_unique_id_use_t use);
- static void acquireAudioSessionId(audio_session_t audioSession, pid_t pid);
+ static void acquireAudioSessionId(audio_session_t audioSession, pid_t pid, uid_t uid);
static void releaseAudioSessionId(audio_session_t audioSession, pid_t pid);
// Get the HW synchronization source used for an audio session.
@@ -279,6 +289,8 @@
static uint32_t getStrategyForStream(audio_stream_type_t stream);
static audio_devices_t getDevicesForStream(audio_stream_type_t stream);
+ static status_t getDevicesForAttributes(const AudioAttributes &aa,
+ AudioDeviceTypeAddrVector *devices);
static audio_io_handle_t getOutputForEffect(const effect_descriptor_t *desc);
static status_t registerEffect(const effect_descriptor_t *desc,
@@ -302,6 +314,8 @@
static status_t setLowRamDevice(bool isLowRamDevice, int64_t totalMemory);
+ static status_t setSupportedSystemUsages(const std::vector<audio_usage_t>& systemUsages);
+
static status_t setAllowedCapturePolicy(uid_t uid, audio_flags_mask_t flags);
// Check if hw offload is possible for given format, stream type, sample rate,
@@ -350,6 +364,10 @@
static status_t removeUidDeviceAffinities(uid_t uid);
+ static status_t setUserIdDeviceAffinities(int userId, const Vector<AudioDeviceTypeAddr>& devices);
+
+ static status_t removeUserIdDeviceAffinities(int userId);
+
static status_t startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,
audio_port_handle_t *portId);
@@ -560,15 +578,19 @@
static const sp<AudioFlingerClient> getAudioFlingerClient();
static sp<AudioIoDescriptor> getIoDescriptor(audio_io_handle_t ioHandle);
+ // Invokes all registered error callbacks with the given error code.
+ static void reportError(status_t err);
+
static sp<AudioFlingerClient> gAudioFlingerClient;
static sp<AudioPolicyServiceClient> gAudioPolicyServiceClient;
friend class AudioFlingerClient;
friend class AudioPolicyServiceClient;
- static Mutex gLock; // protects gAudioFlinger and gAudioErrorCallback,
+ static Mutex gLock; // protects gAudioFlinger
+ static Mutex gLockErrorCallbacks; // protects gAudioErrorCallbacks
static Mutex gLockAPS; // protects gAudioPolicyService and gAudioPolicyServiceClient
static sp<IAudioFlinger> gAudioFlinger;
- static audio_error_callback gAudioErrorCallback;
+ static std::set<audio_error_callback> gAudioErrorCallbacks;
static dynamic_policy_callback gDynPolicyCallback;
static record_config_callback gRecordConfigCallback;
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index df5eabc..95cad0a 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -22,7 +22,7 @@
#include <media/AudioTimestamp.h>
#include <media/IAudioTrack.h>
#include <media/AudioResamplerPublic.h>
-#include <media/MediaAnalyticsItem.h>
+#include <media/MediaMetricsItem.h>
#include <media/Modulo.h>
#include <utils/threads.h>
@@ -107,6 +107,11 @@
int16_t* i16; // signed 16-bit
int8_t* i8; // unsigned 8-bit, offset by 0x80
}; // input to obtainBuffer(): unused, output: pointer to buffer
+
+ uint32_t sequence; // IAudioTrack instance sequence number, as of obtainBuffer().
+ // It is set by obtainBuffer() and confirmed by releaseBuffer().
+ // Not "user-serviceable".
+ // TODO Consider sp<IMemory> instead, or in addition to this.
};
/* As a convenience, if a callback is supplied, a handler thread
@@ -400,7 +405,7 @@
/*
* return metrics information for the current track.
*/
- status_t getMetrics(MediaAnalyticsItem * &item);
+ status_t getMetrics(mediametrics::Item * &item);
/* After it's created the track is not active. Call start() to
* make it active. If set, the callback will start being called.
@@ -692,14 +697,17 @@
* frameCount number of [empty slots for] frames requested
* size ignored
* raw ignored
+ * sequence ignored
* After error return:
* frameCount 0
* size 0
* raw undefined
+ * sequence undefined
* After successful return:
* frameCount actual number of [empty slots for] frames available, <= number requested
* size actual number of bytes available
* raw pointer to the buffer
+ * sequence IAudioTrack instance sequence number, as of obtainBuffer()
*/
status_t obtainBuffer(Buffer* audioBuffer, int32_t waitCount,
size_t *nonContig = NULL);
@@ -1230,21 +1238,22 @@
private:
class MediaMetrics {
public:
- MediaMetrics() : mAnalyticsItem(MediaAnalyticsItem::create("audiotrack")) {
+ MediaMetrics() : mMetricsItem(mediametrics::Item::create("audiotrack")) {
}
~MediaMetrics() {
- // mAnalyticsItem alloc failure will be flagged in the constructor
+ // mMetricsItem alloc failure will be flagged in the constructor
// don't log empty records
- if (mAnalyticsItem->count() > 0) {
- mAnalyticsItem->selfrecord();
+ if (mMetricsItem->count() > 0) {
+ mMetricsItem->selfrecord();
}
}
void gather(const AudioTrack *track);
- MediaAnalyticsItem *dup() { return mAnalyticsItem->dup(); }
+ mediametrics::Item *dup() { return mMetricsItem->dup(); }
private:
- std::unique_ptr<MediaAnalyticsItem> mAnalyticsItem;
+ std::unique_ptr<mediametrics::Item> mMetricsItem;
};
MediaMetrics mMediaMetrics;
+ std::string mMetricsId; // GUARDED_BY(mLock), could change in createTrack_l().
};
}; // namespace android
diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h
index 8703f55..8bc1d83 100644
--- a/media/libaudioclient/include/media/IAudioFlinger.h
+++ b/media/libaudioclient/include/media/IAudioFlinger.h
@@ -446,7 +446,7 @@
virtual audio_unique_id_t newAudioUniqueId(audio_unique_id_use_t use) = 0;
- virtual void acquireAudioSessionId(audio_session_t audioSession, pid_t pid) = 0;
+ virtual void acquireAudioSessionId(audio_session_t audioSession, pid_t pid, uid_t uid) = 0;
virtual void releaseAudioSessionId(audio_session_t audioSession, pid_t pid) = 0;
virtual status_t queryNumberEffects(uint32_t *numEffects) const = 0;
diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h
index 6623061..caa1d13 100644
--- a/media/libaudioclient/include/media/IAudioPolicyService.h
+++ b/media/libaudioclient/include/media/IAudioPolicyService.h
@@ -108,6 +108,8 @@
virtual uint32_t getStrategyForStream(audio_stream_type_t stream) = 0;
virtual audio_devices_t getDevicesForStream(audio_stream_type_t stream) = 0;
+ virtual status_t getDevicesForAttributes(const AudioAttributes &aa,
+ AudioDeviceTypeAddrVector *devices) const = 0;
virtual audio_io_handle_t getOutputForEffect(const effect_descriptor_t *desc) = 0;
virtual status_t registerEffect(const effect_descriptor_t *desc,
audio_io_handle_t io,
@@ -138,6 +140,7 @@
audio_unique_id_t* id) = 0;
virtual status_t removeSourceDefaultEffect(audio_unique_id_t id) = 0;
virtual status_t removeStreamDefaultEffect(audio_unique_id_t id) = 0;
+ virtual status_t setSupportedSystemUsages(const std::vector<audio_usage_t>& systemUsages) = 0;
virtual status_t setAllowedCapturePolicy(uid_t uid, audio_flags_mask_t flags) = 0;
// Check if offload is possible for given format, stream type, sample rate,
// bit rate, duration, video and streaming or offload property is enabled
@@ -192,6 +195,11 @@
virtual status_t removeUidDeviceAffinities(uid_t uid) = 0;
+ virtual status_t setUserIdDeviceAffinities(int userId,
+ const Vector<AudioDeviceTypeAddr>& devices) = 0;
+
+ virtual status_t removeUserIdDeviceAffinities(int userId) = 0;
+
virtual status_t startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,
audio_port_handle_t *portId) = 0;
diff --git a/media/libaudioclient/tests/Android.bp b/media/libaudioclient/tests/Android.bp
index d509be6..350a780 100644
--- a/media/libaudioclient/tests/Android.bp
+++ b/media/libaudioclient/tests/Android.bp
@@ -13,6 +13,7 @@
"test_create_utils.cpp"],
header_libs: [
"libmedia_headers",
+ "libmediametrics_headers",
],
shared_libs: [
"libaudioclient",
@@ -30,6 +31,7 @@
"test_create_utils.cpp"],
header_libs: [
"libmedia_headers",
+ "libmediametrics_headers",
],
shared_libs: [
"libaudioclient",
diff --git a/media/libaudiofoundation/AudioDeviceTypeAddr.cpp b/media/libaudiofoundation/AudioDeviceTypeAddr.cpp
index d390467..b44043a 100644
--- a/media/libaudiofoundation/AudioDeviceTypeAddr.cpp
+++ b/media/libaudiofoundation/AudioDeviceTypeAddr.cpp
@@ -26,6 +26,16 @@
return mType == other.mType && mAddress == other.mAddress;
}
+bool AudioDeviceTypeAddr::operator<(const AudioDeviceTypeAddr& other) const {
+ if (mType < other.mType) return true;
+ if (mType > other.mType) return false;
+
+ if (mAddress < other.mAddress) return true;
+ // if (mAddress > other.mAddress) return false;
+
+ return false;
+}
+
void AudioDeviceTypeAddr::reset() {
mType = AUDIO_DEVICE_NONE;
mAddress = "";
diff --git a/media/libaudiofoundation/include/media/AudioDeviceTypeAddr.h b/media/libaudiofoundation/include/media/AudioDeviceTypeAddr.h
index acc37ca..60ea78e 100644
--- a/media/libaudiofoundation/include/media/AudioDeviceTypeAddr.h
+++ b/media/libaudiofoundation/include/media/AudioDeviceTypeAddr.h
@@ -39,6 +39,8 @@
AudioDeviceTypeAddr& operator= (const AudioDeviceTypeAddr&) = default;
+ bool operator<(const AudioDeviceTypeAddr& other) const;
+
void reset();
status_t readFromParcel(const Parcel *parcel) override;
diff --git a/media/libaudiohal/impl/DeviceHalHidl.cpp b/media/libaudiohal/impl/DeviceHalHidl.cpp
index cd3ac1f..7d0d83d 100644
--- a/media/libaudiohal/impl/DeviceHalHidl.cpp
+++ b/media/libaudiohal/impl/DeviceHalHidl.cpp
@@ -28,6 +28,7 @@
#include <common/all-versions/VersionUtils.h>
#include "DeviceHalHidl.h"
+#include "EffectHalHidl.h"
#include "HidlUtils.h"
#include "StreamHalHidl.h"
#include "VersionUtils.h"
@@ -43,6 +44,8 @@
using namespace ::android::hardware::audio::common::CPP_VERSION;
using namespace ::android::hardware::audio::CPP_VERSION;
+using EffectHalHidl = ::android::effect::CPP_VERSION::EffectHalHidl;
+
namespace {
status_t deviceAddressFromHal(
@@ -323,25 +326,45 @@
if (mDevice == 0) return NO_INIT;
if (patch == nullptr) return BAD_VALUE;
+#if MAJOR_VERSION < 6
if (*patch != AUDIO_PATCH_HANDLE_NONE) {
status_t status = releaseAudioPatch(*patch);
ALOGW_IF(status != NO_ERROR, "%s error %d releasing patch handle %d",
__func__, status, *patch);
+ *patch = AUDIO_PATCH_HANDLE_NONE;
}
+#endif
hidl_vec<AudioPortConfig> hidlSources, hidlSinks;
HidlUtils::audioPortConfigsFromHal(num_sources, sources, &hidlSources);
HidlUtils::audioPortConfigsFromHal(num_sinks, sinks, &hidlSinks);
- Result retval;
- Return<void> ret = mDevice->createAudioPatch(
- hidlSources, hidlSinks,
- [&](Result r, AudioPatchHandle hidlPatch) {
- retval = r;
- if (retval == Result::OK) {
- *patch = static_cast<audio_patch_handle_t>(hidlPatch);
- }
- });
- return processReturn("createAudioPatch", ret, retval);
+ Result retval = Result::OK;
+ Return<void> ret;
+ std::string methodName = "createAudioPatch";
+ if (*patch == AUDIO_PATCH_HANDLE_NONE) { // always true for MAJOR_VERSION < 6
+ ret = mDevice->createAudioPatch(
+ hidlSources, hidlSinks,
+ [&](Result r, AudioPatchHandle hidlPatch) {
+ retval = r;
+ if (retval == Result::OK) {
+ *patch = static_cast<audio_patch_handle_t>(hidlPatch);
+ }
+ });
+ } else {
+#if MAJOR_VERSION >= 6
+ ret = mDevice->updateAudioPatch(
+ *patch,
+ hidlSources, hidlSinks,
+ [&](Result r, AudioPatchHandle hidlPatch) {
+ retval = r;
+ if (retval == Result::OK) {
+ *patch = static_cast<audio_patch_handle_t>(hidlPatch);
+ }
+ });
+ methodName = "updateAudioPatch";
+#endif
+ }
+ return processReturn(methodName.c_str(), ret, retval);
}
status_t DeviceHalHidl::releaseAudioPatch(audio_patch_handle_t patch) {
@@ -397,6 +420,36 @@
}
#endif
+#if MAJOR_VERSION >= 6
+status_t DeviceHalHidl::addDeviceEffect(
+ audio_port_handle_t device, sp<EffectHalInterface> effect) {
+ if (mDevice == 0) return NO_INIT;
+ return processReturn("addDeviceEffect", mDevice->addDeviceEffect(
+ static_cast<AudioPortHandle>(device),
+ static_cast<EffectHalHidl*>(effect.get())->effectId()));
+}
+#else
+status_t DeviceHalHidl::addDeviceEffect(
+ audio_port_handle_t device __unused, sp<EffectHalInterface> effect __unused) {
+ return INVALID_OPERATION;
+}
+#endif
+
+#if MAJOR_VERSION >= 6
+status_t DeviceHalHidl::removeDeviceEffect(
+ audio_port_handle_t device, sp<EffectHalInterface> effect) {
+ if (mDevice == 0) return NO_INIT;
+ return processReturn("removeDeviceEffect", mDevice->removeDeviceEffect(
+ static_cast<AudioPortHandle>(device),
+ static_cast<EffectHalHidl*>(effect.get())->effectId()));
+}
+#else
+status_t DeviceHalHidl::removeDeviceEffect(
+ audio_port_handle_t device __unused, sp<EffectHalInterface> effect __unused) {
+ return INVALID_OPERATION;
+}
+#endif
+
status_t DeviceHalHidl::dump(int fd) {
if (mDevice == 0) return NO_INIT;
native_handle_t* hidlHandle = native_handle_create(1, 0);
diff --git a/media/libaudiohal/impl/DeviceHalHidl.h b/media/libaudiohal/impl/DeviceHalHidl.h
index f7d465f..d342d4a 100644
--- a/media/libaudiohal/impl/DeviceHalHidl.h
+++ b/media/libaudiohal/impl/DeviceHalHidl.h
@@ -113,6 +113,9 @@
// List microphones
virtual status_t getMicrophones(std::vector<media::MicrophoneInfo> *microphones);
+ status_t addDeviceEffect(audio_port_handle_t device, sp<EffectHalInterface> effect) override;
+ status_t removeDeviceEffect(audio_port_handle_t device, sp<EffectHalInterface> effect) override;
+
virtual status_t dump(int fd);
private:
diff --git a/media/libaudiohal/impl/DeviceHalLocal.cpp b/media/libaudiohal/impl/DeviceHalLocal.cpp
index dfbb6b2..8021d92 100644
--- a/media/libaudiohal/impl/DeviceHalLocal.cpp
+++ b/media/libaudiohal/impl/DeviceHalLocal.cpp
@@ -206,6 +206,17 @@
}
#endif
+// Local HAL implementation does not support effects
+status_t DeviceHalLocal::addDeviceEffect(
+ audio_port_handle_t device __unused, sp<EffectHalInterface> effect __unused) {
+ return INVALID_OPERATION;
+}
+
+status_t DeviceHalLocal::removeDeviceEffect(
+ audio_port_handle_t device __unused, sp<EffectHalInterface> effect __unused) {
+ return INVALID_OPERATION;
+}
+
status_t DeviceHalLocal::dump(int fd) {
return mDev->dump(mDev, fd);
}
diff --git a/media/libaudiohal/impl/DeviceHalLocal.h b/media/libaudiohal/impl/DeviceHalLocal.h
index 36db72e..d85e2a7 100644
--- a/media/libaudiohal/impl/DeviceHalLocal.h
+++ b/media/libaudiohal/impl/DeviceHalLocal.h
@@ -106,6 +106,9 @@
// List microphones
virtual status_t getMicrophones(std::vector<media::MicrophoneInfo> *microphones);
+ status_t addDeviceEffect(audio_port_handle_t device, sp<EffectHalInterface> effect) override;
+ status_t removeDeviceEffect(audio_port_handle_t device, sp<EffectHalInterface> effect) override;
+
virtual status_t dump(int fd);
void closeOutputStream(struct audio_stream_out *stream_out);
diff --git a/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h b/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h
index 2200a7f..1e04b21 100644
--- a/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h
+++ b/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_HARDWARE_DEVICE_HAL_INTERFACE_H
#define ANDROID_HARDWARE_DEVICE_HAL_INTERFACE_H
+#include <media/audiohal/EffectHalInterface.h>
#include <media/MicrophoneInfo.h>
#include <system/audio.h>
#include <utils/Errors.h>
@@ -111,6 +112,11 @@
// List microphones
virtual status_t getMicrophones(std::vector<media::MicrophoneInfo> *microphones) = 0;
+ virtual status_t addDeviceEffect(
+ audio_port_handle_t device, sp<EffectHalInterface> effect) = 0;
+ virtual status_t removeDeviceEffect(
+ audio_port_handle_t device, sp<EffectHalInterface> effect) = 0;
+
virtual status_t dump(int fd) = 0;
protected:
diff --git a/media/libaudioprocessing/Android.bp b/media/libaudioprocessing/Android.bp
index 9b5d58c..1a6a5a2 100644
--- a/media/libaudioprocessing/Android.bp
+++ b/media/libaudioprocessing/Android.bp
@@ -32,6 +32,7 @@
],
header_libs: [
+ "libaudiohal_headers",
"libbase_headers",
"libmedia_headers"
],
diff --git a/media/libdatasource/FileSource.cpp b/media/libdatasource/FileSource.cpp
index bbf7dda..3d34d0c 100644
--- a/media/libdatasource/FileSource.cpp
+++ b/media/libdatasource/FileSource.cpp
@@ -107,6 +107,9 @@
Mutex::Autolock autoLock(mLock);
if (mLength >= 0) {
+ if (offset < 0) {
+ return UNKNOWN_ERROR;
+ }
if (offset >= mLength) {
return 0; // read beyond EOF.
}
diff --git a/media/libeffects/config/Android.bp b/media/libeffects/config/Android.bp
index 5fa9da9..8476f82 100644
--- a/media/libeffects/config/Android.bp
+++ b/media/libeffects/config/Android.bp
@@ -13,6 +13,8 @@
shared_libs: [
"liblog",
"libtinyxml2",
+ "libutils",
+ "libmedia_helper",
],
header_libs: ["libaudio_system_headers"],
diff --git a/media/libeffects/config/include/media/EffectsConfig.h b/media/libeffects/config/include/media/EffectsConfig.h
index fa0415b..ef10e0d 100644
--- a/media/libeffects/config/include/media/EffectsConfig.h
+++ b/media/libeffects/config/include/media/EffectsConfig.h
@@ -76,6 +76,10 @@
using OutputStream = Stream<audio_stream_type_t>;
using InputStream = Stream<audio_source_t>;
+struct DeviceEffects : Stream<audio_devices_t> {
+ std::string address;
+};
+
/** Parsed configuration.
* Intended to be a transient structure only used for deserialization.
* Note: Everything is copied in the configuration from the xml dom.
@@ -89,6 +93,7 @@
Effects effects;
std::vector<OutputStream> postprocess;
std::vector<InputStream> preprocess;
+ std::vector<DeviceEffects> deviceprocess;
};
/** Result of `parse(const char*)` */
diff --git a/media/libeffects/config/src/EffectsConfig.cpp b/media/libeffects/config/src/EffectsConfig.cpp
index f39eb0c..85fbf11 100644
--- a/media/libeffects/config/src/EffectsConfig.cpp
+++ b/media/libeffects/config/src/EffectsConfig.cpp
@@ -26,6 +26,7 @@
#include <log/log.h>
#include <media/EffectsConfig.h>
+#include <media/TypeConverter.h>
using namespace tinyxml2;
@@ -100,6 +101,7 @@
{AUDIO_STREAM_ENFORCED_AUDIBLE, "enforced_audible"},
{AUDIO_STREAM_DTMF, "dtmf"},
{AUDIO_STREAM_TTS, "tts"},
+ {AUDIO_STREAM_ASSISTANT, "assistant"},
};
/** All input stream types which support effects.
@@ -116,6 +118,8 @@
{AUDIO_SOURCE_VOICE_COMMUNICATION, "voice_communication"},
{AUDIO_SOURCE_UNPROCESSED, "unprocessed"},
{AUDIO_SOURCE_VOICE_PERFORMANCE, "voice_performance"},
+ {AUDIO_SOURCE_ECHO_REFERENCE, "echo_reference"},
+ {AUDIO_SOURCE_FM_TUNER, "fm_tuner"},
};
/** Find the stream type enum corresponding to the stream type name or return false */
@@ -131,6 +135,11 @@
return false;
}
+template <>
+bool stringToStreamType(const char *streamName, audio_devices_t* type) {
+ return deviceFromString(streamName, *type);
+}
+
/** Parse a library xml note and push the result in libraries or return false on failure. */
bool parseLibrary(const XMLElement& xmlLibrary, Libraries* libraries) {
const char* name = xmlLibrary.Attribute("name");
@@ -218,7 +227,7 @@
return true;
}
-/** Parse an stream from an xml element describing it.
+/** Parse an <Output|Input>stream or a device from an xml element describing it.
* @return true and pushes the stream in streams on success,
* false on failure. */
template <class Stream>
@@ -230,14 +239,14 @@
}
Stream stream;
if (!stringToStreamType(streamType, &stream.type)) {
- ALOGE("Invalid stream type %s: %s", streamType, dump(xmlStream));
+ ALOGE("Invalid <stream|device> type %s: %s", streamType, dump(xmlStream));
return false;
}
for (auto& xmlApply : getChildren(xmlStream, "apply")) {
const char* effectName = xmlApply.get().Attribute("effect");
if (effectName == nullptr) {
- ALOGE("stream/apply must have reference an effect: %s", dump(xmlApply));
+ ALOGE("<stream|device>/apply must have reference an effect: %s", dump(xmlApply));
return false;
}
auto* effect = findByName(effectName, effects);
@@ -251,6 +260,21 @@
return true;
}
+bool parseDeviceEffects(
+ const XMLElement& xmlDevice, Effects& effects, std::vector<DeviceEffects>* deviceEffects) {
+
+ const char* address = xmlDevice.Attribute("address");
+ if (address == nullptr) {
+ ALOGE("device must have an address: %s", dump(xmlDevice));
+ return false;
+ }
+ if (!parseStream(xmlDevice, effects, deviceEffects)) {
+ return false;
+ }
+ deviceEffects->back().address = address;
+ return true;
+}
+
/** Internal version of the public parse(const char* path) where path always exist. */
ParsingResult parseWithPath(std::string&& path) {
XMLDocument doc;
@@ -295,6 +319,14 @@
registerFailure(parseStream(xmlStream, config->effects, &config->postprocess));
}
}
+
+ // Parse device effect chains
+ for (auto& xmlDeviceEffects : getChildren(xmlConfig, "deviceEffects")) {
+ for (auto& xmlDevice : getChildren(xmlDeviceEffects, "devicePort")) {
+ registerFailure(
+ parseDeviceEffects(xmlDevice, config->effects, &config->deviceprocess));
+ }
+ }
}
return {std::move(config), nbSkippedElements, std::move(path)};
}
diff --git a/media/libeffects/data/audio_effects.xml b/media/libeffects/data/audio_effects.xml
index 3f85052..2e5f529 100644
--- a/media/libeffects/data/audio_effects.xml
+++ b/media/libeffects/data/audio_effects.xml
@@ -99,4 +99,31 @@
</postprocess>
-->
+ <!-- Device pre/post processor configurations.
+ The device pre/post processor configuration is described in a deviceEffects element and
+ consists in a list of elements each describing pre/post proecessor settings for a given
+ device or "devicePort".
+ Each devicePort element has a "type" attribute corresponding to the device type (e.g.
+ speaker, bus), an "address" attribute corresponding to the device address and contains a
+ list of "apply" elements indicating one effect to apply.
+ If the device is a source, only pre processing effects are expected, if the
+ device is a sink, only post processing effects are expected.
+ The effect to apply is designated by its name in the "effects" elements.
+ The effect will be enabled by default and the audio framework will automatically add
+ and activate the effect if the given port is involved in an audio patch.
+ If the patch is "HW", the effect must be HW accelerated.
+
+ <deviceEffects>
+ <devicePort type="AUDIO_DEVICE_OUT_BUS" address="BUS00_USAGE_MAIN">
+ <apply effect="equalizer"/>
+ </devicePort>
+ <devicePort type="AUDIO_DEVICE_OUT_BUS" address="BUS04_USAGE_VOICE">
+ <apply effect="volume"/>
+ </devicePort>
+ <devicePort type="AUDIO_DEVICE_IN_BUILTIN_MIC" address="bottom">
+ <apply effect="agc"/>
+ </devicePort>
+ </deviceEffects>
+ -->
+
</audio_effects_conf>
diff --git a/media/libeffects/lvm/lib/Android.bp b/media/libeffects/lvm/lib/Android.bp
index d150f18..6d998d1 100644
--- a/media/libeffects/lvm/lib/Android.bp
+++ b/media/libeffects/lvm/lib/Android.bp
@@ -10,107 +10,107 @@
vendor: true,
srcs: [
- "StereoWidening/src/LVCS_BypassMix.c",
- "StereoWidening/src/LVCS_Control.c",
- "StereoWidening/src/LVCS_Equaliser.c",
- "StereoWidening/src/LVCS_Init.c",
- "StereoWidening/src/LVCS_Process.c",
- "StereoWidening/src/LVCS_ReverbGenerator.c",
- "StereoWidening/src/LVCS_StereoEnhancer.c",
- "StereoWidening/src/LVCS_Tables.c",
- "Bass/src/LVDBE_Control.c",
- "Bass/src/LVDBE_Init.c",
- "Bass/src/LVDBE_Process.c",
- "Bass/src/LVDBE_Tables.c",
- "Bundle/src/LVM_API_Specials.c",
- "Bundle/src/LVM_Buffers.c",
- "Bundle/src/LVM_Init.c",
- "Bundle/src/LVM_Process.c",
- "Bundle/src/LVM_Tables.c",
- "Bundle/src/LVM_Control.c",
- "SpectrumAnalyzer/src/LVPSA_Control.c",
- "SpectrumAnalyzer/src/LVPSA_Init.c",
- "SpectrumAnalyzer/src/LVPSA_Memory.c",
- "SpectrumAnalyzer/src/LVPSA_Process.c",
- "SpectrumAnalyzer/src/LVPSA_QPD_Init.c",
- "SpectrumAnalyzer/src/LVPSA_QPD_Process.c",
- "SpectrumAnalyzer/src/LVPSA_Tables.c",
- "Eq/src/LVEQNB_CalcCoef.c",
- "Eq/src/LVEQNB_Control.c",
- "Eq/src/LVEQNB_Init.c",
- "Eq/src/LVEQNB_Process.c",
- "Eq/src/LVEQNB_Tables.c",
- "Common/src/InstAlloc.c",
- "Common/src/DC_2I_D16_TRC_WRA_01.c",
- "Common/src/DC_2I_D16_TRC_WRA_01_Init.c",
- "Common/src/FO_2I_D16F32C15_LShx_TRC_WRA_01.c",
- "Common/src/FO_2I_D16F32Css_LShx_TRC_WRA_01_Init.c",
- "Common/src/FO_1I_D16F16C15_TRC_WRA_01.c",
- "Common/src/FO_1I_D16F16Css_TRC_WRA_01_Init.c",
- "Common/src/BP_1I_D16F32C30_TRC_WRA_01.c",
- "Common/src/BP_1I_D16F16C14_TRC_WRA_01.c",
- "Common/src/BP_1I_D32F32C30_TRC_WRA_02.c",
- "Common/src/BP_1I_D16F16Css_TRC_WRA_01_Init.c",
- "Common/src/BP_1I_D16F32Cll_TRC_WRA_01_Init.c",
- "Common/src/BP_1I_D32F32Cll_TRC_WRA_02_Init.c",
- "Common/src/BQ_2I_D32F32Cll_TRC_WRA_01_Init.c",
- "Common/src/BQ_2I_D32F32C30_TRC_WRA_01.c",
- "Common/src/BQ_2I_D16F32C15_TRC_WRA_01.c",
- "Common/src/BQ_2I_D16F32C14_TRC_WRA_01.c",
- "Common/src/BQ_2I_D16F32C13_TRC_WRA_01.c",
- "Common/src/BQ_2I_D16F32Css_TRC_WRA_01_init.c",
- "Common/src/BQ_2I_D16F16C15_TRC_WRA_01.c",
- "Common/src/BQ_2I_D16F16C14_TRC_WRA_01.c",
- "Common/src/BQ_2I_D16F16Css_TRC_WRA_01_Init.c",
- "Common/src/BQ_1I_D16F16C15_TRC_WRA_01.c",
- "Common/src/BQ_1I_D16F16Css_TRC_WRA_01_Init.c",
- "Common/src/BQ_1I_D16F32C14_TRC_WRA_01.c",
- "Common/src/BQ_1I_D16F32Css_TRC_WRA_01_init.c",
- "Common/src/PK_2I_D32F32C30G11_TRC_WRA_01.c",
- "Common/src/PK_2I_D32F32C14G11_TRC_WRA_01.c",
- "Common/src/PK_2I_D32F32CssGss_TRC_WRA_01_Init.c",
- "Common/src/PK_2I_D32F32CllGss_TRC_WRA_01_Init.c",
- "Common/src/Int16LShiftToInt32_16x32.c",
- "Common/src/From2iToMono_16.c",
- "Common/src/Copy_16.c",
- "Common/src/MonoTo2I_16.c",
- "Common/src/MonoTo2I_32.c",
- "Common/src/LoadConst_16.c",
- "Common/src/LoadConst_32.c",
- "Common/src/dB_to_Lin32.c",
- "Common/src/Shift_Sat_v16xv16.c",
- "Common/src/Shift_Sat_v32xv32.c",
- "Common/src/Abs_32.c",
- "Common/src/Int32RShiftToInt16_Sat_32x16.c",
- "Common/src/From2iToMono_32.c",
- "Common/src/mult3s_16x16.c",
- "Common/src/Mult3s_32x16.c",
- "Common/src/NonLinComp_D16.c",
- "Common/src/DelayMix_16x16.c",
- "Common/src/MSTo2i_Sat_16x16.c",
- "Common/src/From2iToMS_16x16.c",
- "Common/src/Mac3s_Sat_16x16.c",
- "Common/src/Mac3s_Sat_32x16.c",
- "Common/src/Add2_Sat_16x16.c",
- "Common/src/Add2_Sat_32x32.c",
- "Common/src/LVC_MixSoft_1St_2i_D16C31_SAT.c",
- "Common/src/LVC_MixSoft_1St_D16C31_SAT.c",
- "Common/src/LVC_Mixer_VarSlope_SetTimeConstant.c",
- "Common/src/LVC_Mixer_SetTimeConstant.c",
- "Common/src/LVC_Mixer_SetTarget.c",
- "Common/src/LVC_Mixer_GetTarget.c",
- "Common/src/LVC_Mixer_Init.c",
- "Common/src/LVC_Core_MixHard_1St_2i_D16C31_SAT.c",
- "Common/src/LVC_Core_MixSoft_1St_2i_D16C31_WRA.c",
- "Common/src/LVC_Core_MixInSoft_D16C31_SAT.c",
- "Common/src/LVC_Mixer_GetCurrent.c",
- "Common/src/LVC_MixSoft_2St_D16C31_SAT.c",
- "Common/src/LVC_Core_MixSoft_1St_D16C31_WRA.c",
- "Common/src/LVC_Core_MixHard_2St_D16C31_SAT.c",
- "Common/src/LVC_MixInSoft_D16C31_SAT.c",
- "Common/src/AGC_MIX_VOL_2St1Mon_D32_WRA.c",
- "Common/src/LVM_Timer.c",
- "Common/src/LVM_Timer_Init.c",
+ "StereoWidening/src/LVCS_BypassMix.cpp",
+ "StereoWidening/src/LVCS_Control.cpp",
+ "StereoWidening/src/LVCS_Equaliser.cpp",
+ "StereoWidening/src/LVCS_Init.cpp",
+ "StereoWidening/src/LVCS_Process.cpp",
+ "StereoWidening/src/LVCS_ReverbGenerator.cpp",
+ "StereoWidening/src/LVCS_StereoEnhancer.cpp",
+ "StereoWidening/src/LVCS_Tables.cpp",
+ "Bass/src/LVDBE_Control.cpp",
+ "Bass/src/LVDBE_Init.cpp",
+ "Bass/src/LVDBE_Process.cpp",
+ "Bass/src/LVDBE_Tables.cpp",
+ "Bundle/src/LVM_API_Specials.cpp",
+ "Bundle/src/LVM_Buffers.cpp",
+ "Bundle/src/LVM_Init.cpp",
+ "Bundle/src/LVM_Process.cpp",
+ "Bundle/src/LVM_Tables.cpp",
+ "Bundle/src/LVM_Control.cpp",
+ "SpectrumAnalyzer/src/LVPSA_Control.cpp",
+ "SpectrumAnalyzer/src/LVPSA_Init.cpp",
+ "SpectrumAnalyzer/src/LVPSA_Memory.cpp",
+ "SpectrumAnalyzer/src/LVPSA_Process.cpp",
+ "SpectrumAnalyzer/src/LVPSA_QPD_Init.cpp",
+ "SpectrumAnalyzer/src/LVPSA_QPD_Process.cpp",
+ "SpectrumAnalyzer/src/LVPSA_Tables.cpp",
+ "Eq/src/LVEQNB_CalcCoef.cpp",
+ "Eq/src/LVEQNB_Control.cpp",
+ "Eq/src/LVEQNB_Init.cpp",
+ "Eq/src/LVEQNB_Process.cpp",
+ "Eq/src/LVEQNB_Tables.cpp",
+ "Common/src/InstAlloc.cpp",
+ "Common/src/DC_2I_D16_TRC_WRA_01.cpp",
+ "Common/src/DC_2I_D16_TRC_WRA_01_Init.cpp",
+ "Common/src/FO_2I_D16F32C15_LShx_TRC_WRA_01.cpp",
+ "Common/src/FO_2I_D16F32Css_LShx_TRC_WRA_01_Init.cpp",
+ "Common/src/FO_1I_D16F16C15_TRC_WRA_01.cpp",
+ "Common/src/FO_1I_D16F16Css_TRC_WRA_01_Init.cpp",
+ "Common/src/BP_1I_D16F32C30_TRC_WRA_01.cpp",
+ "Common/src/BP_1I_D16F16C14_TRC_WRA_01.cpp",
+ "Common/src/BP_1I_D32F32C30_TRC_WRA_02.cpp",
+ "Common/src/BP_1I_D16F16Css_TRC_WRA_01_Init.cpp",
+ "Common/src/BP_1I_D16F32Cll_TRC_WRA_01_Init.cpp",
+ "Common/src/BP_1I_D32F32Cll_TRC_WRA_02_Init.cpp",
+ "Common/src/BQ_2I_D32F32Cll_TRC_WRA_01_Init.cpp",
+ "Common/src/BQ_2I_D32F32C30_TRC_WRA_01.cpp",
+ "Common/src/BQ_2I_D16F32C15_TRC_WRA_01.cpp",
+ "Common/src/BQ_2I_D16F32C14_TRC_WRA_01.cpp",
+ "Common/src/BQ_2I_D16F32C13_TRC_WRA_01.cpp",
+ "Common/src/BQ_2I_D16F32Css_TRC_WRA_01_init.cpp",
+ "Common/src/BQ_2I_D16F16C15_TRC_WRA_01.cpp",
+ "Common/src/BQ_2I_D16F16C14_TRC_WRA_01.cpp",
+ "Common/src/BQ_2I_D16F16Css_TRC_WRA_01_Init.cpp",
+ "Common/src/BQ_1I_D16F16C15_TRC_WRA_01.cpp",
+ "Common/src/BQ_1I_D16F16Css_TRC_WRA_01_Init.cpp",
+ "Common/src/BQ_1I_D16F32C14_TRC_WRA_01.cpp",
+ "Common/src/BQ_1I_D16F32Css_TRC_WRA_01_init.cpp",
+ "Common/src/PK_2I_D32F32C30G11_TRC_WRA_01.cpp",
+ "Common/src/PK_2I_D32F32C14G11_TRC_WRA_01.cpp",
+ "Common/src/PK_2I_D32F32CssGss_TRC_WRA_01_Init.cpp",
+ "Common/src/PK_2I_D32F32CllGss_TRC_WRA_01_Init.cpp",
+ "Common/src/Int16LShiftToInt32_16x32.cpp",
+ "Common/src/From2iToMono_16.cpp",
+ "Common/src/Copy_16.cpp",
+ "Common/src/MonoTo2I_16.cpp",
+ "Common/src/MonoTo2I_32.cpp",
+ "Common/src/LoadConst_16.cpp",
+ "Common/src/LoadConst_32.cpp",
+ "Common/src/dB_to_Lin32.cpp",
+ "Common/src/Shift_Sat_v16xv16.cpp",
+ "Common/src/Shift_Sat_v32xv32.cpp",
+ "Common/src/Abs_32.cpp",
+ "Common/src/Int32RShiftToInt16_Sat_32x16.cpp",
+ "Common/src/From2iToMono_32.cpp",
+ "Common/src/mult3s_16x16.cpp",
+ "Common/src/Mult3s_32x16.cpp",
+ "Common/src/NonLinComp_D16.cpp",
+ "Common/src/DelayMix_16x16.cpp",
+ "Common/src/MSTo2i_Sat_16x16.cpp",
+ "Common/src/From2iToMS_16x16.cpp",
+ "Common/src/Mac3s_Sat_16x16.cpp",
+ "Common/src/Mac3s_Sat_32x16.cpp",
+ "Common/src/Add2_Sat_16x16.cpp",
+ "Common/src/Add2_Sat_32x32.cpp",
+ "Common/src/LVC_MixSoft_1St_2i_D16C31_SAT.cpp",
+ "Common/src/LVC_MixSoft_1St_D16C31_SAT.cpp",
+ "Common/src/LVC_Mixer_VarSlope_SetTimeConstant.cpp",
+ "Common/src/LVC_Mixer_SetTimeConstant.cpp",
+ "Common/src/LVC_Mixer_SetTarget.cpp",
+ "Common/src/LVC_Mixer_GetTarget.cpp",
+ "Common/src/LVC_Mixer_Init.cpp",
+ "Common/src/LVC_Core_MixHard_1St_2i_D16C31_SAT.cpp",
+ "Common/src/LVC_Core_MixSoft_1St_2i_D16C31_WRA.cpp",
+ "Common/src/LVC_Core_MixInSoft_D16C31_SAT.cpp",
+ "Common/src/LVC_Mixer_GetCurrent.cpp",
+ "Common/src/LVC_MixSoft_2St_D16C31_SAT.cpp",
+ "Common/src/LVC_Core_MixSoft_1St_D16C31_WRA.cpp",
+ "Common/src/LVC_Core_MixHard_2St_D16C31_SAT.cpp",
+ "Common/src/LVC_MixInSoft_D16C31_SAT.cpp",
+ "Common/src/AGC_MIX_VOL_2St1Mon_D32_WRA.cpp",
+ "Common/src/LVM_Timer.cpp",
+ "Common/src/LVM_Timer_Init.cpp",
],
local_include_dirs: [
@@ -135,7 +135,7 @@
header_libs: [
"libhardware_headers"
],
- cflags: [
+ cppflags: [
"-fvisibility=hidden",
"-DBUILD_FLOAT",
"-DHIGHER_FS",
@@ -159,42 +159,42 @@
vendor: true,
srcs: [
- "Reverb/src/LVREV_ApplyNewSettings.c",
- "Reverb/src/LVREV_ClearAudioBuffers.c",
- "Reverb/src/LVREV_GetControlParameters.c",
- "Reverb/src/LVREV_GetInstanceHandle.c",
- "Reverb/src/LVREV_GetMemoryTable.c",
- "Reverb/src/LVREV_Process.c",
- "Reverb/src/LVREV_SetControlParameters.c",
- "Reverb/src/LVREV_Tables.c",
- "Common/src/Abs_32.c",
- "Common/src/InstAlloc.c",
- "Common/src/LoadConst_16.c",
- "Common/src/LoadConst_32.c",
- "Common/src/From2iToMono_32.c",
- "Common/src/Mult3s_32x16.c",
- "Common/src/FO_1I_D32F32C31_TRC_WRA_01.c",
- "Common/src/FO_1I_D32F32Cll_TRC_WRA_01_Init.c",
- "Common/src/DelayAllPass_Sat_32x16To32.c",
- "Common/src/Copy_16.c",
- "Common/src/Mac3s_Sat_32x16.c",
- "Common/src/DelayWrite_32.c",
- "Common/src/Shift_Sat_v32xv32.c",
- "Common/src/Add2_Sat_32x32.c",
- "Common/src/JoinTo2i_32x32.c",
- "Common/src/MonoTo2I_32.c",
- "Common/src/LVM_FO_HPF.c",
- "Common/src/LVM_FO_LPF.c",
- "Common/src/LVM_Polynomial.c",
- "Common/src/LVM_Power10.c",
- "Common/src/LVM_GetOmega.c",
- "Common/src/MixSoft_2St_D32C31_SAT.c",
- "Common/src/MixSoft_1St_D32C31_WRA.c",
- "Common/src/MixInSoft_D32C31_SAT.c",
- "Common/src/LVM_Mixer_TimeConstant.c",
- "Common/src/Core_MixHard_2St_D32C31_SAT.c",
- "Common/src/Core_MixSoft_1St_D32C31_WRA.c",
- "Common/src/Core_MixInSoft_D32C31_SAT.c",
+ "Reverb/src/LVREV_ApplyNewSettings.cpp",
+ "Reverb/src/LVREV_ClearAudioBuffers.cpp",
+ "Reverb/src/LVREV_GetControlParameters.cpp",
+ "Reverb/src/LVREV_GetInstanceHandle.cpp",
+ "Reverb/src/LVREV_GetMemoryTable.cpp",
+ "Reverb/src/LVREV_Process.cpp",
+ "Reverb/src/LVREV_SetControlParameters.cpp",
+ "Reverb/src/LVREV_Tables.cpp",
+ "Common/src/Abs_32.cpp",
+ "Common/src/InstAlloc.cpp",
+ "Common/src/LoadConst_16.cpp",
+ "Common/src/LoadConst_32.cpp",
+ "Common/src/From2iToMono_32.cpp",
+ "Common/src/Mult3s_32x16.cpp",
+ "Common/src/FO_1I_D32F32C31_TRC_WRA_01.cpp",
+ "Common/src/FO_1I_D32F32Cll_TRC_WRA_01_Init.cpp",
+ "Common/src/DelayAllPass_Sat_32x16To32.cpp",
+ "Common/src/Copy_16.cpp",
+ "Common/src/Mac3s_Sat_32x16.cpp",
+ "Common/src/DelayWrite_32.cpp",
+ "Common/src/Shift_Sat_v32xv32.cpp",
+ "Common/src/Add2_Sat_32x32.cpp",
+ "Common/src/JoinTo2i_32x32.cpp",
+ "Common/src/MonoTo2I_32.cpp",
+ "Common/src/LVM_FO_HPF.cpp",
+ "Common/src/LVM_FO_LPF.cpp",
+ "Common/src/LVM_Polynomial.cpp",
+ "Common/src/LVM_Power10.cpp",
+ "Common/src/LVM_GetOmega.cpp",
+ "Common/src/MixSoft_2St_D32C31_SAT.cpp",
+ "Common/src/MixSoft_1St_D32C31_WRA.cpp",
+ "Common/src/MixInSoft_D32C31_SAT.cpp",
+ "Common/src/LVM_Mixer_TimeConstant.cpp",
+ "Common/src/Core_MixHard_2St_D32C31_SAT.cpp",
+ "Common/src/Core_MixSoft_1St_D32C31_WRA.cpp",
+ "Common/src/Core_MixInSoft_D32C31_SAT.cpp",
],
local_include_dirs: [
@@ -206,7 +206,7 @@
"Common/lib",
],
- cflags: [
+ cppflags: [
"-fvisibility=hidden",
"-DBUILD_FLOAT",
"-DHIGHER_FS",
diff --git a/media/libeffects/lvm/lib/Bass/lib/LVDBE.h b/media/libeffects/lvm/lib/Bass/lib/LVDBE.h
index cc066b0..261a21a 100644
--- a/media/libeffects/lvm/lib/Bass/lib/LVDBE.h
+++ b/media/libeffects/lvm/lib/Bass/lib/LVDBE.h
@@ -55,9 +55,6 @@
#ifndef __LVDBE_H__
#define __LVDBE_H__
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
/****************************************************************************************/
@@ -477,8 +474,5 @@
LVM_UINT16 NumSamples);
#endif
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif /* __LVDBE_H__ */
diff --git a/media/libeffects/lvm/lib/Bass/src/LVDBE_Control.c b/media/libeffects/lvm/lib/Bass/src/LVDBE_Control.cpp
similarity index 98%
rename from media/libeffects/lvm/lib/Bass/src/LVDBE_Control.c
rename to media/libeffects/lvm/lib/Bass/src/LVDBE_Control.cpp
index 0ba2c86..513c67a 100644
--- a/media/libeffects/lvm/lib/Bass/src/LVDBE_Control.c
+++ b/media/libeffects/lvm/lib/Bass/src/LVDBE_Control.cpp
@@ -131,7 +131,7 @@
sizeof(pInstance->pData->HPFTaps)/sizeof(LVM_INT16)); /* Number of words */
#else
LoadConst_Float(0, /* Clear the history, value 0 */
- (void *)&pInstance->pData->HPFTaps, /* Destination Cast to void: \
+ (LVM_FLOAT *)&pInstance->pData->HPFTaps, /* Destination Cast to void: \
no dereferencing in function*/
sizeof(pInstance->pData->HPFTaps) / sizeof(LVM_FLOAT)); /* Number of words */
#endif
@@ -156,7 +156,7 @@
sizeof(pInstance->pData->BPFTaps)/sizeof(LVM_INT16)); /* Number of words */
#else
LoadConst_Float(0, /* Clear the history, value 0 */
- (void *)&pInstance->pData->BPFTaps, /* Destination Cast to void: \
+ (LVM_FLOAT *)&pInstance->pData->BPFTaps, /* Destination Cast to void: \
no dereferencing in function*/
sizeof(pInstance->pData->BPFTaps) / sizeof(LVM_FLOAT)); /* Number of words */
#endif
diff --git a/media/libeffects/lvm/lib/Bass/src/LVDBE_Init.c b/media/libeffects/lvm/lib/Bass/src/LVDBE_Init.cpp
similarity index 97%
rename from media/libeffects/lvm/lib/Bass/src/LVDBE_Init.c
rename to media/libeffects/lvm/lib/Bass/src/LVDBE_Init.cpp
index 2946734..a5500ba 100644
--- a/media/libeffects/lvm/lib/Bass/src/LVDBE_Init.c
+++ b/media/libeffects/lvm/lib/Bass/src/LVDBE_Init.cpp
@@ -232,8 +232,10 @@
/*
* Set pointer to data and coef memory
*/
- pInstance->pData = pMemoryTable->Region[LVDBE_MEMREGION_PERSISTENT_DATA].pBaseAddress;
- pInstance->pCoef = pMemoryTable->Region[LVDBE_MEMREGION_PERSISTENT_COEF].pBaseAddress;
+ pInstance->pData =
+ (LVDBE_Data_FLOAT_t *)pMemoryTable->Region[LVDBE_MEMREGION_PERSISTENT_DATA].pBaseAddress;
+ pInstance->pCoef =
+ (LVDBE_Coef_FLOAT_t *)pMemoryTable->Region[LVDBE_MEMREGION_PERSISTENT_COEF].pBaseAddress;
/*
diff --git a/media/libeffects/lvm/lib/Bass/src/LVDBE_Private.h b/media/libeffects/lvm/lib/Bass/src/LVDBE_Private.h
index 4225a30..458e9e8 100644
--- a/media/libeffects/lvm/lib/Bass/src/LVDBE_Private.h
+++ b/media/libeffects/lvm/lib/Bass/src/LVDBE_Private.h
@@ -27,9 +27,6 @@
#ifndef __LVDBE_PRIVATE_H__
#define __LVDBE_PRIVATE_H__
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
/****************************************************************************************/
@@ -164,8 +161,5 @@
LVDBE_Params_t *pParams);
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif /* __LVDBE_PRIVATE_H__ */
diff --git a/media/libeffects/lvm/lib/Bass/src/LVDBE_Process.c b/media/libeffects/lvm/lib/Bass/src/LVDBE_Process.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Bass/src/LVDBE_Process.c
rename to media/libeffects/lvm/lib/Bass/src/LVDBE_Process.cpp
diff --git a/media/libeffects/lvm/lib/Bass/src/LVDBE_Tables.c b/media/libeffects/lvm/lib/Bass/src/LVDBE_Tables.cpp
similarity index 99%
rename from media/libeffects/lvm/lib/Bass/src/LVDBE_Tables.c
rename to media/libeffects/lvm/lib/Bass/src/LVDBE_Tables.cpp
index a2ce404..058dcf6 100644
--- a/media/libeffects/lvm/lib/Bass/src/LVDBE_Tables.c
+++ b/media/libeffects/lvm/lib/Bass/src/LVDBE_Tables.cpp
@@ -24,6 +24,7 @@
#include "LVDBE.h"
#include "LVDBE_Coeffs.h" /* Filter coefficients */
+#include "LVDBE_Tables.h"
#include "BIQUAD.h"
diff --git a/media/libeffects/lvm/lib/Bass/src/LVDBE_Tables.h b/media/libeffects/lvm/lib/Bass/src/LVDBE_Tables.h
index ca46e37..fea09f3 100644
--- a/media/libeffects/lvm/lib/Bass/src/LVDBE_Tables.h
+++ b/media/libeffects/lvm/lib/Bass/src/LVDBE_Tables.h
@@ -24,9 +24,6 @@
#ifndef __LVBDE_TABLES_H__
#define __LVBDE_TABLES_H__
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
#include "BIQUAD.h"
#include "LVM_Types.h"
@@ -128,8 +125,5 @@
extern const LVM_INT16 LVDBE_MixerTCTable[];
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif /* __LVBDE_TABLES_H__ */
diff --git a/media/libeffects/lvm/lib/Bundle/lib/LVM.h b/media/libeffects/lvm/lib/Bundle/lib/LVM.h
index 5082a53..3c089a0 100644
--- a/media/libeffects/lvm/lib/Bundle/lib/LVM.h
+++ b/media/libeffects/lvm/lib/Bundle/lib/LVM.h
@@ -53,9 +53,6 @@
#ifndef __LVM_H__
#define __LVM_H__
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
/****************************************************************************************/
@@ -633,9 +630,6 @@
LVM_ControlParams_t *pParams);
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif /* __LVM_H__ */
diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_API_Specials.c b/media/libeffects/lvm/lib/Bundle/src/LVM_API_Specials.cpp
similarity index 98%
rename from media/libeffects/lvm/lib/Bundle/src/LVM_API_Specials.c
rename to media/libeffects/lvm/lib/Bundle/src/LVM_API_Specials.cpp
index 07b7f0e..62d9ee3 100644
--- a/media/libeffects/lvm/lib/Bundle/src/LVM_API_Specials.c
+++ b/media/libeffects/lvm/lib/Bundle/src/LVM_API_Specials.cpp
@@ -72,7 +72,7 @@
return LVM_SUCCESS;
}
- hPSAInstance = pInstance->hPSAInstance;
+ hPSAInstance = (pLVPSA_Handle_t *)pInstance->hPSAInstance;
if((pCurrentPeaks == LVM_NULL) ||
(pPastPeaks == LVM_NULL))
diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Buffers.c b/media/libeffects/lvm/lib/Bundle/src/LVM_Buffers.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Bundle/src/LVM_Buffers.c
rename to media/libeffects/lvm/lib/Bundle/src/LVM_Buffers.cpp
diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Control.c b/media/libeffects/lvm/lib/Bundle/src/LVM_Control.cpp
similarity index 97%
rename from media/libeffects/lvm/lib/Bundle/src/LVM_Control.c
rename to media/libeffects/lvm/lib/Bundle/src/LVM_Control.cpp
index 1b27cb4..ab1c078 100644
--- a/media/libeffects/lvm/lib/Bundle/src/LVM_Control.c
+++ b/media/libeffects/lvm/lib/Bundle/src/LVM_Control.cpp
@@ -198,7 +198,7 @@
/*
* PSA parameters
*/
- if( (pParams->PSA_PeakDecayRate > LVPSA_SPEED_HIGH) ||
+ if (((LVPSA_LevelDetectSpeed_en)pParams->PSA_PeakDecayRate > LVPSA_SPEED_HIGH) ||
(pParams->PSA_Enable > LVM_PSA_ON))
{
return (LVM_OUTOFRANGE);
@@ -333,7 +333,7 @@
* Clear the taps
*/
LoadConst_Float((LVM_FLOAT)0, /* Value */
- (void *)&pInstance->pTE_Taps->TrebleBoost_Taps, /* Destination.\
+ (LVM_FLOAT *)&pInstance->pTE_Taps->TrebleBoost_Taps, /* Destination.\
Cast to void: no dereferencing in function */
(LVM_UINT16)(sizeof(pInstance->pTE_Taps->TrebleBoost_Taps) / \
sizeof(LVM_FLOAT))); /* Number of words */
@@ -514,7 +514,8 @@
LVM_INT16 MaxGain = 0;
- if ((pParams->EQNB_OperatingMode == LVEQNB_ON) && (pInstance->HeadroomParams.Headroom_OperatingMode == LVM_HEADROOM_ON))
+ if (((LVEQNB_Mode_en)pParams->EQNB_OperatingMode == LVEQNB_ON)
+ && (pInstance->HeadroomParams.Headroom_OperatingMode == LVM_HEADROOM_ON))
{
/* Find typical headroom value */
for(jj = 0; jj < pInstance->HeadroomParams.NHeadroomBands; jj++)
@@ -717,7 +718,7 @@
{
LVDBE_ReturnStatus_en DBE_Status;
LVDBE_Params_t DBE_Params;
- LVDBE_Handle_t *hDBEInstance = pInstance->hDBEInstance;
+ LVDBE_Handle_t *hDBEInstance = (LVDBE_Handle_t *)pInstance->hDBEInstance;
/*
@@ -770,7 +771,7 @@
{
LVEQNB_ReturnStatus_en EQNB_Status;
LVEQNB_Params_t EQNB_Params;
- LVEQNB_Handle_t *hEQNBInstance = pInstance->hEQNBInstance;
+ LVEQNB_Handle_t *hEQNBInstance = (LVEQNB_Handle_t *)pInstance->hEQNBInstance;
/*
@@ -847,7 +848,7 @@
{
LVCS_ReturnStatus_en CS_Status;
LVCS_Params_t CS_Params;
- LVCS_Handle_t *hCSInstance = pInstance->hCSInstance;
+ LVCS_Handle_t *hCSInstance = (LVCS_Handle_t *)pInstance->hCSInstance;
LVM_Mode_en CompressorMode=LVM_MODE_ON;
/*
@@ -898,8 +899,8 @@
/*
* Set the control flag
*/
- if ((LocalParams.OperatingMode == LVM_MODE_ON) &&
- (LocalParams.VirtualizerOperatingMode != LVCS_OFF))
+ if (((LVM_Mode_en)LocalParams.OperatingMode == LVM_MODE_ON) &&
+ ((LVCS_Modes_en)LocalParams.VirtualizerOperatingMode != LVCS_OFF))
{
pInstance->CS_Active = LVM_TRUE;
}
@@ -933,7 +934,7 @@
{
LVPSA_RETURN PSA_Status;
LVPSA_ControlParams_t PSA_Params;
- pLVPSA_Handle_t *hPSAInstance = pInstance->hPSAInstance;
+ pLVPSA_Handle_t *hPSAInstance = (pLVPSA_Handle_t *)pInstance->hPSAInstance;
/*
diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Init.c b/media/libeffects/lvm/lib/Bundle/src/LVM_Init.cpp
similarity index 96%
rename from media/libeffects/lvm/lib/Bundle/src/LVM_Init.c
rename to media/libeffects/lvm/lib/Bundle/src/LVM_Init.cpp
index c57498e..d773910 100644
--- a/media/libeffects/lvm/lib/Bundle/src/LVM_Init.c
+++ b/media/libeffects/lvm/lib/Bundle/src/LVM_Init.cpp
@@ -634,7 +634,8 @@
/*
* Managed buffers required
*/
- pInstance->pBufferManagement = InstAlloc_AddMember(&AllocMem[LVM_MEMREGION_PERSISTENT_SLOW_DATA],
+ pInstance->pBufferManagement = (LVM_Buffer_t *)
+ InstAlloc_AddMember(&AllocMem[LVM_MEMREGION_PERSISTENT_SLOW_DATA],
sizeof(LVM_Buffer_t));
#ifdef BUILD_FLOAT
BundleScratchSize = (LVM_INT32)
@@ -644,8 +645,10 @@
#else
BundleScratchSize = (LVM_INT32)(6 * (MIN_INTERNAL_BLOCKSIZE + InternalBlockSize) * sizeof(LVM_INT16));
#endif
- pInstance->pBufferManagement->pScratch = InstAlloc_AddMember(&AllocMem[LVM_MEMREGION_TEMPORARY_FAST], /* Scratch 1 buffer */
- (LVM_UINT32)BundleScratchSize);
+ pInstance->pBufferManagement->pScratch = (LVM_FLOAT *)
+ InstAlloc_AddMember(
+ &AllocMem[LVM_MEMREGION_TEMPORARY_FAST], /* Scratch 1 buffer */
+ (LVM_UINT32)BundleScratchSize);
#ifdef BUILD_FLOAT
LoadConst_Float(0, /* Clear the input delay buffer */
(LVM_FLOAT *)&pInstance->pBufferManagement->InDelayBuffer,
@@ -760,10 +763,12 @@
/*
* Set the default EQNB pre-gain and pointer to the band definitions
*/
- pInstance->pEQNB_BandDefs = InstAlloc_AddMember(&AllocMem[LVM_MEMREGION_PERSISTENT_FAST_DATA],
- (pInstParams->EQNB_NumBands * sizeof(LVM_EQNB_BandDef_t)));
- pInstance->pEQNB_UserDefs = InstAlloc_AddMember(&AllocMem[LVM_MEMREGION_PERSISTENT_FAST_DATA],
- (pInstParams->EQNB_NumBands * sizeof(LVM_EQNB_BandDef_t)));
+ pInstance->pEQNB_BandDefs =
+ (LVM_EQNB_BandDef_t *)InstAlloc_AddMember(&AllocMem[LVM_MEMREGION_PERSISTENT_FAST_DATA],
+ (pInstParams->EQNB_NumBands * sizeof(LVM_EQNB_BandDef_t)));
+ pInstance->pEQNB_UserDefs =
+ (LVM_EQNB_BandDef_t *)InstAlloc_AddMember(&AllocMem[LVM_MEMREGION_PERSISTENT_FAST_DATA],
+ (pInstParams->EQNB_NumBands * sizeof(LVM_EQNB_BandDef_t)));
/*
@@ -954,10 +959,12 @@
* Headroom management memory allocation
*/
{
- pInstance->pHeadroom_BandDefs = InstAlloc_AddMember(&AllocMem[LVM_MEMREGION_PERSISTENT_FAST_DATA],
- (LVM_HEADROOM_MAX_NBANDS * sizeof(LVM_HeadroomBandDef_t)));
- pInstance->pHeadroom_UserDefs = InstAlloc_AddMember(&AllocMem[LVM_MEMREGION_PERSISTENT_FAST_DATA],
- (LVM_HEADROOM_MAX_NBANDS * sizeof(LVM_HeadroomBandDef_t)));
+ pInstance->pHeadroom_BandDefs = (LVM_HeadroomBandDef_t *)
+ InstAlloc_AddMember(&AllocMem[LVM_MEMREGION_PERSISTENT_FAST_DATA],
+ (LVM_HEADROOM_MAX_NBANDS * sizeof(LVM_HeadroomBandDef_t)));
+ pInstance->pHeadroom_UserDefs = (LVM_HeadroomBandDef_t *)
+ InstAlloc_AddMember(&AllocMem[LVM_MEMREGION_PERSISTENT_FAST_DATA],
+ (LVM_HEADROOM_MAX_NBANDS * sizeof(LVM_HeadroomBandDef_t)));
/* Headroom management parameters initialisation */
pInstance->NewHeadroomParams.NHeadroomBands = 2;
@@ -1022,7 +1029,7 @@
/* Fast Temporary */
#ifdef BUILD_FLOAT
- pInstance->pPSAInput = InstAlloc_AddMember(&AllocMem[LVM_TEMPORARY_FAST],
+ pInstance->pPSAInput = (LVM_FLOAT *)InstAlloc_AddMember(&AllocMem[LVM_TEMPORARY_FAST],
(LVM_UINT32) MAX_INTERNAL_BLOCKSIZE * \
sizeof(LVM_FLOAT));
#else
diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Private.h b/media/libeffects/lvm/lib/Bundle/src/LVM_Private.h
index cdd3134..2bae702 100644
--- a/media/libeffects/lvm/lib/Bundle/src/LVM_Private.h
+++ b/media/libeffects/lvm/lib/Bundle/src/LVM_Private.h
@@ -27,9 +27,6 @@
#ifndef __LVM_PRIVATE_H__
#define __LVM_PRIVATE_H__
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
/************************************************************************************/
@@ -344,9 +341,6 @@
void *pData,
LVM_INT16 callbackId);
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif /* __LVM_PRIVATE_H__ */
diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Process.c b/media/libeffects/lvm/lib/Bundle/src/LVM_Process.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Bundle/src/LVM_Process.c
rename to media/libeffects/lvm/lib/Bundle/src/LVM_Process.cpp
diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Tables.c b/media/libeffects/lvm/lib/Bundle/src/LVM_Tables.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Bundle/src/LVM_Tables.c
rename to media/libeffects/lvm/lib/Bundle/src/LVM_Tables.cpp
diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Tables.h b/media/libeffects/lvm/lib/Bundle/src/LVM_Tables.h
index 4cf7119..3fd2f89 100644
--- a/media/libeffects/lvm/lib/Bundle/src/LVM_Tables.h
+++ b/media/libeffects/lvm/lib/Bundle/src/LVM_Tables.h
@@ -18,9 +18,6 @@
#ifndef __LVM_TABLES_H__
#define __LVM_TABLES_H__
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
/************************************************************************************/
/* */
@@ -57,9 +54,6 @@
extern const LVM_INT16 LVM_MixerTCTable[];
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif /* __LVM_TABLES_H__ */
diff --git a/media/libeffects/lvm/lib/Common/lib/AGC.h b/media/libeffects/lvm/lib/Common/lib/AGC.h
index 06e742e..f75d983 100644
--- a/media/libeffects/lvm/lib/Common/lib/AGC.h
+++ b/media/libeffects/lvm/lib/Common/lib/AGC.h
@@ -19,9 +19,6 @@
#define __AGC_H__
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
/**********************************************************************************/
/* */
@@ -94,9 +91,6 @@
LVM_INT32 *pDst, /* Stereo destination */
LVM_UINT16 n); /* Number of samples */
#endif
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif /* __AGC_H__ */
diff --git a/media/libeffects/lvm/lib/Common/lib/BIQUAD.h b/media/libeffects/lvm/lib/Common/lib/BIQUAD.h
index 01539b2..2baba7c 100644
--- a/media/libeffects/lvm/lib/Common/lib/BIQUAD.h
+++ b/media/libeffects/lvm/lib/Common/lib/BIQUAD.h
@@ -19,9 +19,6 @@
#define _BIQUAD_H_
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
#include "LVM_Types.h"
/**********************************************************************************
@@ -604,9 +601,6 @@
LVM_INT16 *pDataOut,
LVM_INT16 NrSamples);
#endif
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
/**********************************************************************************/
diff --git a/media/libeffects/lvm/lib/Common/lib/CompLim.h b/media/libeffects/lvm/lib/Common/lib/CompLim.h
index 498faa3..4e7addd 100644
--- a/media/libeffects/lvm/lib/Common/lib/CompLim.h
+++ b/media/libeffects/lvm/lib/Common/lib/CompLim.h
@@ -18,9 +18,6 @@
#ifndef _COMP_LIM_H
#define _COMP_LIM_H
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
/************************************************************************************/
@@ -77,9 +74,6 @@
LVM_INT16 *pSterBfOut,
LVM_INT32 BlockLength);
#endif
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif /* #ifndef _COMP_LIM_H */
diff --git a/media/libeffects/lvm/lib/Common/lib/Filter.h b/media/libeffects/lvm/lib/Common/lib/Filter.h
index 0c8955d..3133ce2 100644
--- a/media/libeffects/lvm/lib/Common/lib/Filter.h
+++ b/media/libeffects/lvm/lib/Common/lib/Filter.h
@@ -18,9 +18,6 @@
#ifndef _FILTER_H_
#define _FILTER_H_
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
/**********************************************************************************
INCLUDES
@@ -75,9 +72,6 @@
LVM_Fs_en SampleRate);
#endif
/**********************************************************************************/
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif /** _FILTER_H_ **/
diff --git a/media/libeffects/lvm/lib/Common/lib/InstAlloc.h b/media/libeffects/lvm/lib/Common/lib/InstAlloc.h
index 7f725f4..10b5775 100644
--- a/media/libeffects/lvm/lib/Common/lib/InstAlloc.h
+++ b/media/libeffects/lvm/lib/Common/lib/InstAlloc.h
@@ -18,9 +18,6 @@
#ifndef __INSTALLOC_H__
#define __INSTALLOC_H__
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
#include "LVM_Types.h"
/*######################################################################################*/
@@ -85,8 +82,5 @@
void InstAlloc_InitAll_NULL( INST_ALLOC *pms);
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif /* __JBS_INSTALLOC_H__ */
diff --git a/media/libeffects/lvm/lib/Common/lib/LVM_Common.h b/media/libeffects/lvm/lib/Common/lib/LVM_Common.h
index ceccd7b..96da872 100644
--- a/media/libeffects/lvm/lib/Common/lib/LVM_Common.h
+++ b/media/libeffects/lvm/lib/Common/lib/LVM_Common.h
@@ -27,9 +27,6 @@
#ifndef __LVM_COMMON_H__
#define __LVM_COMMON_H__
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
/****************************************************************************************/
@@ -53,9 +50,6 @@
#define ALGORITHM_VC_ID 0x0500
#define ALGORITHM_TE_ID 0x0600
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif /* __LVM_COMMON_H__ */
diff --git a/media/libeffects/lvm/lib/Common/lib/LVM_Macros.h b/media/libeffects/lvm/lib/Common/lib/LVM_Macros.h
index 97d13a5..2ecc7f8 100644
--- a/media/libeffects/lvm/lib/Common/lib/LVM_Macros.h
+++ b/media/libeffects/lvm/lib/Common/lib/LVM_Macros.h
@@ -18,9 +18,6 @@
#ifndef _LVM_MACROS_H_
#define _LVM_MACROS_H_
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
/**********************************************************************************
MUL32x32INTO32(A,B,C,ShiftR)
@@ -113,9 +110,6 @@
#endif
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif /* _LVM_MACROS_H_ */
diff --git a/media/libeffects/lvm/lib/Common/lib/LVM_Timer.h b/media/libeffects/lvm/lib/Common/lib/LVM_Timer.h
index a76354d..9722bf5 100644
--- a/media/libeffects/lvm/lib/Common/lib/LVM_Timer.h
+++ b/media/libeffects/lvm/lib/Common/lib/LVM_Timer.h
@@ -34,9 +34,6 @@
/****************************************************************************************/
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
/****************************************************************************************/
/* TYPE DEFINITIONS */
@@ -83,8 +80,5 @@
/* END OF HEADER */
/****************************************************************************************/
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif /* __LVM_TIMER_H__ */
diff --git a/media/libeffects/lvm/lib/Common/lib/LVM_Types.h b/media/libeffects/lvm/lib/Common/lib/LVM_Types.h
index fbfdd4d..3eae70e 100644
--- a/media/libeffects/lvm/lib/Common/lib/LVM_Types.h
+++ b/media/libeffects/lvm/lib/Common/lib/LVM_Types.h
@@ -25,9 +25,6 @@
#ifndef LVM_TYPES_H
#define LVM_TYPES_H
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
#include <stdint.h>
@@ -223,8 +220,5 @@
/* */
/****************************************************************************************/
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif /* LVM_TYPES_H */
diff --git a/media/libeffects/lvm/lib/Common/lib/Mixer.h b/media/libeffects/lvm/lib/Common/lib/Mixer.h
index 07c53cd..c63d882 100644
--- a/media/libeffects/lvm/lib/Common/lib/Mixer.h
+++ b/media/libeffects/lvm/lib/Common/lib/Mixer.h
@@ -19,9 +19,6 @@
#define __MIXER_H__
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
#include "LVM_Types.h"
@@ -187,9 +184,6 @@
LVM_INT32 *dst,
LVM_INT16 n);
#endif
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
/**********************************************************************************/
diff --git a/media/libeffects/lvm/lib/Common/lib/ScalarArithmetic.h b/media/libeffects/lvm/lib/Common/lib/ScalarArithmetic.h
index cdb3837..a492a13 100644
--- a/media/libeffects/lvm/lib/Common/lib/ScalarArithmetic.h
+++ b/media/libeffects/lvm/lib/Common/lib/ScalarArithmetic.h
@@ -18,9 +18,6 @@
#ifndef __SCALARARITHMETIC_H__
#define __SCALARARITHMETIC_H__
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
/*######################################################################################*/
@@ -59,9 +56,6 @@
LVM_INT32 dB_to_Lin32(LVM_INT16 db_fix);
#endif
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif /* __SCALARARITHMETIC_H__ */
diff --git a/media/libeffects/lvm/lib/Common/lib/VectorArithmetic.h b/media/libeffects/lvm/lib/Common/lib/VectorArithmetic.h
index 10eedd9..ff789fd 100644
--- a/media/libeffects/lvm/lib/Common/lib/VectorArithmetic.h
+++ b/media/libeffects/lvm/lib/Common/lib/VectorArithmetic.h
@@ -19,9 +19,6 @@
#define _VECTOR_ARITHMETIC_H_
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
#include "LVM_Types.h"
@@ -254,9 +251,6 @@
LVM_INT16 n,
LVM_INT16 shift );
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
/**********************************************************************************/
diff --git a/media/libeffects/lvm/lib/Common/src/AGC_MIX_VOL_2St1Mon_D32_WRA.c b/media/libeffects/lvm/lib/Common/src/AGC_MIX_VOL_2St1Mon_D32_WRA.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/AGC_MIX_VOL_2St1Mon_D32_WRA.c
rename to media/libeffects/lvm/lib/Common/src/AGC_MIX_VOL_2St1Mon_D32_WRA.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/Abs_32.c b/media/libeffects/lvm/lib/Common/src/Abs_32.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/Abs_32.c
rename to media/libeffects/lvm/lib/Common/src/Abs_32.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/Add2_Sat_16x16.c b/media/libeffects/lvm/lib/Common/src/Add2_Sat_16x16.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/Add2_Sat_16x16.c
rename to media/libeffects/lvm/lib/Common/src/Add2_Sat_16x16.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/Add2_Sat_32x32.c b/media/libeffects/lvm/lib/Common/src/Add2_Sat_32x32.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/Add2_Sat_32x32.c
rename to media/libeffects/lvm/lib/Common/src/Add2_Sat_32x32.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/BP_1I_D16F16C14_TRC_WRA_01.c b/media/libeffects/lvm/lib/Common/src/BP_1I_D16F16C14_TRC_WRA_01.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/BP_1I_D16F16C14_TRC_WRA_01.c
rename to media/libeffects/lvm/lib/Common/src/BP_1I_D16F16C14_TRC_WRA_01.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/BP_1I_D16F16Css_TRC_WRA_01_Init.c b/media/libeffects/lvm/lib/Common/src/BP_1I_D16F16Css_TRC_WRA_01_Init.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/BP_1I_D16F16Css_TRC_WRA_01_Init.c
rename to media/libeffects/lvm/lib/Common/src/BP_1I_D16F16Css_TRC_WRA_01_Init.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/BP_1I_D16F32C30_TRC_WRA_01.c b/media/libeffects/lvm/lib/Common/src/BP_1I_D16F32C30_TRC_WRA_01.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/BP_1I_D16F32C30_TRC_WRA_01.c
rename to media/libeffects/lvm/lib/Common/src/BP_1I_D16F32C30_TRC_WRA_01.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/BP_1I_D16F32Cll_TRC_WRA_01_Init.c b/media/libeffects/lvm/lib/Common/src/BP_1I_D16F32Cll_TRC_WRA_01_Init.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/BP_1I_D16F32Cll_TRC_WRA_01_Init.c
rename to media/libeffects/lvm/lib/Common/src/BP_1I_D16F32Cll_TRC_WRA_01_Init.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/BP_1I_D32F32C30_TRC_WRA_02.c b/media/libeffects/lvm/lib/Common/src/BP_1I_D32F32C30_TRC_WRA_02.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/BP_1I_D32F32C30_TRC_WRA_02.c
rename to media/libeffects/lvm/lib/Common/src/BP_1I_D32F32C30_TRC_WRA_02.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/BP_1I_D32F32Cll_TRC_WRA_02_Init.c b/media/libeffects/lvm/lib/Common/src/BP_1I_D32F32Cll_TRC_WRA_02_Init.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/BP_1I_D32F32Cll_TRC_WRA_02_Init.c
rename to media/libeffects/lvm/lib/Common/src/BP_1I_D32F32Cll_TRC_WRA_02_Init.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/BQ_1I_D16F16C15_TRC_WRA_01.c b/media/libeffects/lvm/lib/Common/src/BQ_1I_D16F16C15_TRC_WRA_01.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/BQ_1I_D16F16C15_TRC_WRA_01.c
rename to media/libeffects/lvm/lib/Common/src/BQ_1I_D16F16C15_TRC_WRA_01.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/BQ_1I_D16F16Css_TRC_WRA_01_Init.c b/media/libeffects/lvm/lib/Common/src/BQ_1I_D16F16Css_TRC_WRA_01_Init.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/BQ_1I_D16F16Css_TRC_WRA_01_Init.c
rename to media/libeffects/lvm/lib/Common/src/BQ_1I_D16F16Css_TRC_WRA_01_Init.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/BQ_1I_D16F32C14_TRC_WRA_01.c b/media/libeffects/lvm/lib/Common/src/BQ_1I_D16F32C14_TRC_WRA_01.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/BQ_1I_D16F32C14_TRC_WRA_01.c
rename to media/libeffects/lvm/lib/Common/src/BQ_1I_D16F32C14_TRC_WRA_01.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/BQ_1I_D16F32Css_TRC_WRA_01_init.c b/media/libeffects/lvm/lib/Common/src/BQ_1I_D16F32Css_TRC_WRA_01_init.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/BQ_1I_D16F32Css_TRC_WRA_01_init.c
rename to media/libeffects/lvm/lib/Common/src/BQ_1I_D16F32Css_TRC_WRA_01_init.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/BQ_2I_D16F16C14_TRC_WRA_01.c b/media/libeffects/lvm/lib/Common/src/BQ_2I_D16F16C14_TRC_WRA_01.cpp
similarity index 99%
rename from media/libeffects/lvm/lib/Common/src/BQ_2I_D16F16C14_TRC_WRA_01.c
rename to media/libeffects/lvm/lib/Common/src/BQ_2I_D16F16C14_TRC_WRA_01.cpp
index 9b0fde3..8ee76c9 100644
--- a/media/libeffects/lvm/lib/Common/src/BQ_2I_D16F16C14_TRC_WRA_01.c
+++ b/media/libeffects/lvm/lib/Common/src/BQ_2I_D16F16C14_TRC_WRA_01.cpp
@@ -186,4 +186,4 @@
}
-#endif
\ No newline at end of file
+#endif
diff --git a/media/libeffects/lvm/lib/Common/src/BQ_2I_D16F16C15_TRC_WRA_01.c b/media/libeffects/lvm/lib/Common/src/BQ_2I_D16F16C15_TRC_WRA_01.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/BQ_2I_D16F16C15_TRC_WRA_01.c
rename to media/libeffects/lvm/lib/Common/src/BQ_2I_D16F16C15_TRC_WRA_01.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/BQ_2I_D16F16Css_TRC_WRA_01_Init.c b/media/libeffects/lvm/lib/Common/src/BQ_2I_D16F16Css_TRC_WRA_01_Init.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/BQ_2I_D16F16Css_TRC_WRA_01_Init.c
rename to media/libeffects/lvm/lib/Common/src/BQ_2I_D16F16Css_TRC_WRA_01_Init.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/BQ_2I_D16F32C13_TRC_WRA_01.c b/media/libeffects/lvm/lib/Common/src/BQ_2I_D16F32C13_TRC_WRA_01.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/BQ_2I_D16F32C13_TRC_WRA_01.c
rename to media/libeffects/lvm/lib/Common/src/BQ_2I_D16F32C13_TRC_WRA_01.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/BQ_2I_D16F32C14_TRC_WRA_01.c b/media/libeffects/lvm/lib/Common/src/BQ_2I_D16F32C14_TRC_WRA_01.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/BQ_2I_D16F32C14_TRC_WRA_01.c
rename to media/libeffects/lvm/lib/Common/src/BQ_2I_D16F32C14_TRC_WRA_01.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/BQ_2I_D16F32C15_TRC_WRA_01.c b/media/libeffects/lvm/lib/Common/src/BQ_2I_D16F32C15_TRC_WRA_01.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/BQ_2I_D16F32C15_TRC_WRA_01.c
rename to media/libeffects/lvm/lib/Common/src/BQ_2I_D16F32C15_TRC_WRA_01.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/BQ_2I_D16F32Css_TRC_WRA_01_init.c b/media/libeffects/lvm/lib/Common/src/BQ_2I_D16F32Css_TRC_WRA_01_init.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/BQ_2I_D16F32Css_TRC_WRA_01_init.c
rename to media/libeffects/lvm/lib/Common/src/BQ_2I_D16F32Css_TRC_WRA_01_init.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/BQ_2I_D32F32C30_TRC_WRA_01.c b/media/libeffects/lvm/lib/Common/src/BQ_2I_D32F32C30_TRC_WRA_01.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/BQ_2I_D32F32C30_TRC_WRA_01.c
rename to media/libeffects/lvm/lib/Common/src/BQ_2I_D32F32C30_TRC_WRA_01.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/BQ_2I_D32F32Cll_TRC_WRA_01_Init.c b/media/libeffects/lvm/lib/Common/src/BQ_2I_D32F32Cll_TRC_WRA_01_Init.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/BQ_2I_D32F32Cll_TRC_WRA_01_Init.c
rename to media/libeffects/lvm/lib/Common/src/BQ_2I_D32F32Cll_TRC_WRA_01_Init.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/Copy_16.c b/media/libeffects/lvm/lib/Common/src/Copy_16.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/Copy_16.c
rename to media/libeffects/lvm/lib/Common/src/Copy_16.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/Core_MixHard_2St_D32C31_SAT.c b/media/libeffects/lvm/lib/Common/src/Core_MixHard_2St_D32C31_SAT.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/Core_MixHard_2St_D32C31_SAT.c
rename to media/libeffects/lvm/lib/Common/src/Core_MixHard_2St_D32C31_SAT.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/Core_MixInSoft_D32C31_SAT.c b/media/libeffects/lvm/lib/Common/src/Core_MixInSoft_D32C31_SAT.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/Core_MixInSoft_D32C31_SAT.c
rename to media/libeffects/lvm/lib/Common/src/Core_MixInSoft_D32C31_SAT.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/Core_MixSoft_1St_D32C31_WRA.c b/media/libeffects/lvm/lib/Common/src/Core_MixSoft_1St_D32C31_WRA.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/Core_MixSoft_1St_D32C31_WRA.c
rename to media/libeffects/lvm/lib/Common/src/Core_MixSoft_1St_D32C31_WRA.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/DC_2I_D16_TRC_WRA_01.c b/media/libeffects/lvm/lib/Common/src/DC_2I_D16_TRC_WRA_01.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/DC_2I_D16_TRC_WRA_01.c
rename to media/libeffects/lvm/lib/Common/src/DC_2I_D16_TRC_WRA_01.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/DC_2I_D16_TRC_WRA_01_Init.c b/media/libeffects/lvm/lib/Common/src/DC_2I_D16_TRC_WRA_01_Init.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/DC_2I_D16_TRC_WRA_01_Init.c
rename to media/libeffects/lvm/lib/Common/src/DC_2I_D16_TRC_WRA_01_Init.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/DelayAllPass_Sat_32x16To32.c b/media/libeffects/lvm/lib/Common/src/DelayAllPass_Sat_32x16To32.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/DelayAllPass_Sat_32x16To32.c
rename to media/libeffects/lvm/lib/Common/src/DelayAllPass_Sat_32x16To32.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/DelayMix_16x16.c b/media/libeffects/lvm/lib/Common/src/DelayMix_16x16.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/DelayMix_16x16.c
rename to media/libeffects/lvm/lib/Common/src/DelayMix_16x16.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/DelayWrite_32.c b/media/libeffects/lvm/lib/Common/src/DelayWrite_32.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/DelayWrite_32.c
rename to media/libeffects/lvm/lib/Common/src/DelayWrite_32.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/FO_1I_D16F16C15_TRC_WRA_01.c b/media/libeffects/lvm/lib/Common/src/FO_1I_D16F16C15_TRC_WRA_01.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/FO_1I_D16F16C15_TRC_WRA_01.c
rename to media/libeffects/lvm/lib/Common/src/FO_1I_D16F16C15_TRC_WRA_01.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/FO_1I_D16F16Css_TRC_WRA_01_Init.c b/media/libeffects/lvm/lib/Common/src/FO_1I_D16F16Css_TRC_WRA_01_Init.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/FO_1I_D16F16Css_TRC_WRA_01_Init.c
rename to media/libeffects/lvm/lib/Common/src/FO_1I_D16F16Css_TRC_WRA_01_Init.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/FO_1I_D32F32C31_TRC_WRA_01.c b/media/libeffects/lvm/lib/Common/src/FO_1I_D32F32C31_TRC_WRA_01.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/FO_1I_D32F32C31_TRC_WRA_01.c
rename to media/libeffects/lvm/lib/Common/src/FO_1I_D32F32C31_TRC_WRA_01.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/FO_1I_D32F32Cll_TRC_WRA_01_Init.c b/media/libeffects/lvm/lib/Common/src/FO_1I_D32F32Cll_TRC_WRA_01_Init.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/FO_1I_D32F32Cll_TRC_WRA_01_Init.c
rename to media/libeffects/lvm/lib/Common/src/FO_1I_D32F32Cll_TRC_WRA_01_Init.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/FO_2I_D16F32C15_LShx_TRC_WRA_01.c b/media/libeffects/lvm/lib/Common/src/FO_2I_D16F32C15_LShx_TRC_WRA_01.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/FO_2I_D16F32C15_LShx_TRC_WRA_01.c
rename to media/libeffects/lvm/lib/Common/src/FO_2I_D16F32C15_LShx_TRC_WRA_01.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/FO_2I_D16F32Css_LShx_TRC_WRA_01_Init.c b/media/libeffects/lvm/lib/Common/src/FO_2I_D16F32Css_LShx_TRC_WRA_01_Init.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/FO_2I_D16F32Css_LShx_TRC_WRA_01_Init.c
rename to media/libeffects/lvm/lib/Common/src/FO_2I_D16F32Css_LShx_TRC_WRA_01_Init.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/Filters.h b/media/libeffects/lvm/lib/Common/src/Filters.h
index b1fde0c..14b7226 100644
--- a/media/libeffects/lvm/lib/Common/src/Filters.h
+++ b/media/libeffects/lvm/lib/Common/src/Filters.h
@@ -18,9 +18,6 @@
#ifndef FILTERS_H
#define FILTERS_H
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
#include "LVM_Types.h"
@@ -76,9 +73,6 @@
LVM_UINT16 Scale;
} BiquadA01B1CoefsSP_t;
#endif
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif /* FILTERS_H */
diff --git a/media/libeffects/lvm/lib/Common/src/From2iToMS_16x16.c b/media/libeffects/lvm/lib/Common/src/From2iToMS_16x16.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/From2iToMS_16x16.c
rename to media/libeffects/lvm/lib/Common/src/From2iToMS_16x16.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/From2iToMono_16.c b/media/libeffects/lvm/lib/Common/src/From2iToMono_16.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/From2iToMono_16.c
rename to media/libeffects/lvm/lib/Common/src/From2iToMono_16.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/From2iToMono_32.c b/media/libeffects/lvm/lib/Common/src/From2iToMono_32.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/From2iToMono_32.c
rename to media/libeffects/lvm/lib/Common/src/From2iToMono_32.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/InstAlloc.c b/media/libeffects/lvm/lib/Common/src/InstAlloc.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/InstAlloc.c
rename to media/libeffects/lvm/lib/Common/src/InstAlloc.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/Int16LShiftToInt32_16x32.c b/media/libeffects/lvm/lib/Common/src/Int16LShiftToInt32_16x32.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/Int16LShiftToInt32_16x32.c
rename to media/libeffects/lvm/lib/Common/src/Int16LShiftToInt32_16x32.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/Int32RShiftToInt16_Sat_32x16.c b/media/libeffects/lvm/lib/Common/src/Int32RShiftToInt16_Sat_32x16.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/Int32RShiftToInt16_Sat_32x16.c
rename to media/libeffects/lvm/lib/Common/src/Int32RShiftToInt16_Sat_32x16.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/JoinTo2i_32x32.c b/media/libeffects/lvm/lib/Common/src/JoinTo2i_32x32.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/JoinTo2i_32x32.c
rename to media/libeffects/lvm/lib/Common/src/JoinTo2i_32x32.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/LVC_Core_MixHard_1St_2i_D16C31_SAT.c b/media/libeffects/lvm/lib/Common/src/LVC_Core_MixHard_1St_2i_D16C31_SAT.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/LVC_Core_MixHard_1St_2i_D16C31_SAT.c
rename to media/libeffects/lvm/lib/Common/src/LVC_Core_MixHard_1St_2i_D16C31_SAT.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/LVC_Core_MixHard_2St_D16C31_SAT.c b/media/libeffects/lvm/lib/Common/src/LVC_Core_MixHard_2St_D16C31_SAT.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/LVC_Core_MixHard_2St_D16C31_SAT.c
rename to media/libeffects/lvm/lib/Common/src/LVC_Core_MixHard_2St_D16C31_SAT.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/LVC_Core_MixInSoft_D16C31_SAT.c b/media/libeffects/lvm/lib/Common/src/LVC_Core_MixInSoft_D16C31_SAT.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/LVC_Core_MixInSoft_D16C31_SAT.c
rename to media/libeffects/lvm/lib/Common/src/LVC_Core_MixInSoft_D16C31_SAT.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/LVC_Core_MixSoft_1St_2i_D16C31_WRA.c b/media/libeffects/lvm/lib/Common/src/LVC_Core_MixSoft_1St_2i_D16C31_WRA.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/LVC_Core_MixSoft_1St_2i_D16C31_WRA.c
rename to media/libeffects/lvm/lib/Common/src/LVC_Core_MixSoft_1St_2i_D16C31_WRA.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/LVC_Core_MixSoft_1St_D16C31_WRA.c b/media/libeffects/lvm/lib/Common/src/LVC_Core_MixSoft_1St_D16C31_WRA.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/LVC_Core_MixSoft_1St_D16C31_WRA.c
rename to media/libeffects/lvm/lib/Common/src/LVC_Core_MixSoft_1St_D16C31_WRA.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/LVC_MixInSoft_D16C31_SAT.c b/media/libeffects/lvm/lib/Common/src/LVC_MixInSoft_D16C31_SAT.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/LVC_MixInSoft_D16C31_SAT.c
rename to media/libeffects/lvm/lib/Common/src/LVC_MixInSoft_D16C31_SAT.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/LVC_MixSoft_1St_2i_D16C31_SAT.c b/media/libeffects/lvm/lib/Common/src/LVC_MixSoft_1St_2i_D16C31_SAT.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/LVC_MixSoft_1St_2i_D16C31_SAT.c
rename to media/libeffects/lvm/lib/Common/src/LVC_MixSoft_1St_2i_D16C31_SAT.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/LVC_MixSoft_1St_D16C31_SAT.c b/media/libeffects/lvm/lib/Common/src/LVC_MixSoft_1St_D16C31_SAT.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/LVC_MixSoft_1St_D16C31_SAT.c
rename to media/libeffects/lvm/lib/Common/src/LVC_MixSoft_1St_D16C31_SAT.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/LVC_MixSoft_2St_D16C31_SAT.c b/media/libeffects/lvm/lib/Common/src/LVC_MixSoft_2St_D16C31_SAT.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/LVC_MixSoft_2St_D16C31_SAT.c
rename to media/libeffects/lvm/lib/Common/src/LVC_MixSoft_2St_D16C31_SAT.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/LVC_Mixer.h b/media/libeffects/lvm/lib/Common/src/LVC_Mixer.h
index 199d529..eac9726 100644
--- a/media/libeffects/lvm/lib/Common/src/LVC_Mixer.h
+++ b/media/libeffects/lvm/lib/Common/src/LVC_Mixer.h
@@ -19,9 +19,6 @@
#define __LVC_MIXER_H__
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
#include "LVM_Types.h"
@@ -242,9 +239,6 @@
LVM_INT16 *dst, /* dst can be equal to src */
LVM_INT16 n); /* Number of stereo samples */
#endif
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
/**********************************************************************************/
diff --git a/media/libeffects/lvm/lib/Common/src/LVC_Mixer_GetCurrent.c b/media/libeffects/lvm/lib/Common/src/LVC_Mixer_GetCurrent.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/LVC_Mixer_GetCurrent.c
rename to media/libeffects/lvm/lib/Common/src/LVC_Mixer_GetCurrent.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/LVC_Mixer_GetTarget.c b/media/libeffects/lvm/lib/Common/src/LVC_Mixer_GetTarget.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/LVC_Mixer_GetTarget.c
rename to media/libeffects/lvm/lib/Common/src/LVC_Mixer_GetTarget.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/LVC_Mixer_Init.c b/media/libeffects/lvm/lib/Common/src/LVC_Mixer_Init.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/LVC_Mixer_Init.c
rename to media/libeffects/lvm/lib/Common/src/LVC_Mixer_Init.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/LVC_Mixer_SetTarget.c b/media/libeffects/lvm/lib/Common/src/LVC_Mixer_SetTarget.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/LVC_Mixer_SetTarget.c
rename to media/libeffects/lvm/lib/Common/src/LVC_Mixer_SetTarget.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/LVC_Mixer_SetTimeConstant.c b/media/libeffects/lvm/lib/Common/src/LVC_Mixer_SetTimeConstant.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/LVC_Mixer_SetTimeConstant.c
rename to media/libeffects/lvm/lib/Common/src/LVC_Mixer_SetTimeConstant.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/LVC_Mixer_VarSlope_SetTimeConstant.c b/media/libeffects/lvm/lib/Common/src/LVC_Mixer_VarSlope_SetTimeConstant.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/LVC_Mixer_VarSlope_SetTimeConstant.c
rename to media/libeffects/lvm/lib/Common/src/LVC_Mixer_VarSlope_SetTimeConstant.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/LVM_FO_HPF.c b/media/libeffects/lvm/lib/Common/src/LVM_FO_HPF.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/LVM_FO_HPF.c
rename to media/libeffects/lvm/lib/Common/src/LVM_FO_HPF.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/LVM_FO_LPF.c b/media/libeffects/lvm/lib/Common/src/LVM_FO_LPF.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/LVM_FO_LPF.c
rename to media/libeffects/lvm/lib/Common/src/LVM_FO_LPF.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/LVM_GetOmega.c b/media/libeffects/lvm/lib/Common/src/LVM_GetOmega.cpp
similarity index 99%
rename from media/libeffects/lvm/lib/Common/src/LVM_GetOmega.c
rename to media/libeffects/lvm/lib/Common/src/LVM_GetOmega.cpp
index 6307e68..ed8e1fa 100644
--- a/media/libeffects/lvm/lib/Common/src/LVM_GetOmega.c
+++ b/media/libeffects/lvm/lib/Common/src/LVM_GetOmega.cpp
@@ -36,11 +36,11 @@
const LVM_INT32 LVVDL_2PiOnFsTable[] = {LVVDL_2PiBy_8000 , /* 8kHz in Q41, 16kHz in Q42, 32kHz in Q43 */
LVVDL_2PiBy_11025, /* 11025 Hz in Q41, 22050Hz in Q42, 44100 Hz in Q43*/
LVVDL_2PiBy_12000}; /* 12kHz in Q41, 24kHz in Q42, 48kHz in Q43 */
-#endif
const LVM_INT32 LVVDL_2PiOnFsShiftTable[]={LVVDL_2PiByFs_SHIFT1 , /* 8kHz, 11025Hz, 12kHz */
LVVDL_2PiByFs_SHIFT2, /* 16kHz, 22050Hz, 24kHz*/
LVVDL_2PiByFs_SHIFT3}; /* 32kHz, 44100Hz, 48kHz */
+#endif
#ifdef BUILD_FLOAT
#define LVVDL_2PiBy_8000_f 0.000785398f
#define LVVDL_2PiBy_11025_f 0.000569903f
diff --git a/media/libeffects/lvm/lib/Common/src/LVM_Mixer_TimeConstant.c b/media/libeffects/lvm/lib/Common/src/LVM_Mixer_TimeConstant.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/LVM_Mixer_TimeConstant.c
rename to media/libeffects/lvm/lib/Common/src/LVM_Mixer_TimeConstant.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/LVM_Polynomial.c b/media/libeffects/lvm/lib/Common/src/LVM_Polynomial.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/LVM_Polynomial.c
rename to media/libeffects/lvm/lib/Common/src/LVM_Polynomial.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/LVM_Power10.c b/media/libeffects/lvm/lib/Common/src/LVM_Power10.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/LVM_Power10.c
rename to media/libeffects/lvm/lib/Common/src/LVM_Power10.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/LVM_Timer.c b/media/libeffects/lvm/lib/Common/src/LVM_Timer.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/LVM_Timer.c
rename to media/libeffects/lvm/lib/Common/src/LVM_Timer.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/LVM_Timer_Init.c b/media/libeffects/lvm/lib/Common/src/LVM_Timer_Init.cpp
similarity index 96%
rename from media/libeffects/lvm/lib/Common/src/LVM_Timer_Init.c
rename to media/libeffects/lvm/lib/Common/src/LVM_Timer_Init.cpp
index a935cfe..3015057 100644
--- a/media/libeffects/lvm/lib/Common/src/LVM_Timer_Init.c
+++ b/media/libeffects/lvm/lib/Common/src/LVM_Timer_Init.cpp
@@ -40,7 +40,7 @@
pInstancePr = (LVM_Timer_Instance_Private_t *)pInstance;
pInstancePr->CallBackParam = pParams->CallBackParam;
- pInstancePr->pCallBackParams = pParams->pCallBackParams;
+ pInstancePr->pCallBackParams = (LVM_INT32 *)pParams->pCallBackParams;
pInstancePr->pCallbackInstance = pParams->pCallbackInstance;
pInstancePr->pCallBack = pParams->pCallBack;
pInstancePr->TimerArmed = 1;
diff --git a/media/libeffects/lvm/lib/Common/src/LVM_Timer_Private.h b/media/libeffects/lvm/lib/Common/src/LVM_Timer_Private.h
index 480944f..0dd4272 100644
--- a/media/libeffects/lvm/lib/Common/src/LVM_Timer_Private.h
+++ b/media/libeffects/lvm/lib/Common/src/LVM_Timer_Private.h
@@ -19,9 +19,6 @@
#define LVM_TIMER_PRIVATE_H
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
#include "LVM_Types.h"
@@ -45,8 +42,5 @@
/* END OF HEADER */
/****************************************************************************************/
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif /* LVM_TIMER_PRIVATE_H */
diff --git a/media/libeffects/lvm/lib/Common/src/LoadConst_16.c b/media/libeffects/lvm/lib/Common/src/LoadConst_16.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/LoadConst_16.c
rename to media/libeffects/lvm/lib/Common/src/LoadConst_16.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/LoadConst_32.c b/media/libeffects/lvm/lib/Common/src/LoadConst_32.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/LoadConst_32.c
rename to media/libeffects/lvm/lib/Common/src/LoadConst_32.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/MSTo2i_Sat_16x16.c b/media/libeffects/lvm/lib/Common/src/MSTo2i_Sat_16x16.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/MSTo2i_Sat_16x16.c
rename to media/libeffects/lvm/lib/Common/src/MSTo2i_Sat_16x16.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/Mac3s_Sat_16x16.c b/media/libeffects/lvm/lib/Common/src/Mac3s_Sat_16x16.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/Mac3s_Sat_16x16.c
rename to media/libeffects/lvm/lib/Common/src/Mac3s_Sat_16x16.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/Mac3s_Sat_32x16.c b/media/libeffects/lvm/lib/Common/src/Mac3s_Sat_32x16.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/Mac3s_Sat_32x16.c
rename to media/libeffects/lvm/lib/Common/src/Mac3s_Sat_32x16.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/MixInSoft_D32C31_SAT.c b/media/libeffects/lvm/lib/Common/src/MixInSoft_D32C31_SAT.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/MixInSoft_D32C31_SAT.c
rename to media/libeffects/lvm/lib/Common/src/MixInSoft_D32C31_SAT.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/MixSoft_1St_D32C31_WRA.c b/media/libeffects/lvm/lib/Common/src/MixSoft_1St_D32C31_WRA.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/MixSoft_1St_D32C31_WRA.c
rename to media/libeffects/lvm/lib/Common/src/MixSoft_1St_D32C31_WRA.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/MixSoft_2St_D32C31_SAT.c b/media/libeffects/lvm/lib/Common/src/MixSoft_2St_D32C31_SAT.cpp
similarity index 95%
rename from media/libeffects/lvm/lib/Common/src/MixSoft_2St_D32C31_SAT.c
rename to media/libeffects/lvm/lib/Common/src/MixSoft_2St_D32C31_SAT.cpp
index 6fc1b92..e6faa74 100644
--- a/media/libeffects/lvm/lib/Common/src/MixSoft_2St_D32C31_SAT.c
+++ b/media/libeffects/lvm/lib/Common/src/MixSoft_2St_D32C31_SAT.cpp
@@ -42,7 +42,7 @@
if ((pInstance->Current1 != pInstance->Target1) || (pInstance->Current2 != pInstance->Target2))
{
MixSoft_1St_D32C31_WRA((Mix_1St_Cll_FLOAT_t*)pInstance, src1, dst, n);
- MixInSoft_D32C31_SAT((void *)&pInstance->Alpha2, /* Cast to void: \
+ MixInSoft_D32C31_SAT((Mix_1St_Cll_FLOAT_t *)&pInstance->Alpha2, /* Cast to void: \
no dereferencing in function*/
src2, dst, n);
}
@@ -54,7 +54,8 @@
else
{
if (pInstance->Current1 == 0)
- MixSoft_1St_D32C31_WRA((void *) &pInstance->Alpha2, /* Cast to void: no \
+ MixSoft_1St_D32C31_WRA(
+ (Mix_1St_Cll_FLOAT_t *) &pInstance->Alpha2, /* Cast to void: no \
dereferencing in function*/
src2, dst, n);
else if (pInstance->Current2 == 0)
diff --git a/media/libeffects/lvm/lib/Common/src/MonoTo2I_16.c b/media/libeffects/lvm/lib/Common/src/MonoTo2I_16.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/MonoTo2I_16.c
rename to media/libeffects/lvm/lib/Common/src/MonoTo2I_16.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/MonoTo2I_32.c b/media/libeffects/lvm/lib/Common/src/MonoTo2I_32.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/MonoTo2I_32.c
rename to media/libeffects/lvm/lib/Common/src/MonoTo2I_32.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/Mult3s_32x16.c b/media/libeffects/lvm/lib/Common/src/Mult3s_32x16.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/Mult3s_32x16.c
rename to media/libeffects/lvm/lib/Common/src/Mult3s_32x16.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/NonLinComp_D16.c b/media/libeffects/lvm/lib/Common/src/NonLinComp_D16.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/NonLinComp_D16.c
rename to media/libeffects/lvm/lib/Common/src/NonLinComp_D16.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/PK_2I_D32F32C14G11_TRC_WRA_01.c b/media/libeffects/lvm/lib/Common/src/PK_2I_D32F32C14G11_TRC_WRA_01.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/PK_2I_D32F32C14G11_TRC_WRA_01.c
rename to media/libeffects/lvm/lib/Common/src/PK_2I_D32F32C14G11_TRC_WRA_01.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/PK_2I_D32F32C30G11_TRC_WRA_01.c b/media/libeffects/lvm/lib/Common/src/PK_2I_D32F32C30G11_TRC_WRA_01.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/PK_2I_D32F32C30G11_TRC_WRA_01.c
rename to media/libeffects/lvm/lib/Common/src/PK_2I_D32F32C30G11_TRC_WRA_01.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/PK_2I_D32F32CllGss_TRC_WRA_01_Init.c b/media/libeffects/lvm/lib/Common/src/PK_2I_D32F32CllGss_TRC_WRA_01_Init.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/PK_2I_D32F32CllGss_TRC_WRA_01_Init.c
rename to media/libeffects/lvm/lib/Common/src/PK_2I_D32F32CllGss_TRC_WRA_01_Init.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/PK_2I_D32F32CssGss_TRC_WRA_01_Init.c b/media/libeffects/lvm/lib/Common/src/PK_2I_D32F32CssGss_TRC_WRA_01_Init.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/PK_2I_D32F32CssGss_TRC_WRA_01_Init.c
rename to media/libeffects/lvm/lib/Common/src/PK_2I_D32F32CssGss_TRC_WRA_01_Init.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/Shift_Sat_v16xv16.c b/media/libeffects/lvm/lib/Common/src/Shift_Sat_v16xv16.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/Shift_Sat_v16xv16.c
rename to media/libeffects/lvm/lib/Common/src/Shift_Sat_v16xv16.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/Shift_Sat_v32xv32.c b/media/libeffects/lvm/lib/Common/src/Shift_Sat_v32xv32.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/Shift_Sat_v32xv32.c
rename to media/libeffects/lvm/lib/Common/src/Shift_Sat_v32xv32.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/dB_to_Lin32.c b/media/libeffects/lvm/lib/Common/src/dB_to_Lin32.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/dB_to_Lin32.c
rename to media/libeffects/lvm/lib/Common/src/dB_to_Lin32.cpp
diff --git a/media/libeffects/lvm/lib/Common/src/mult3s_16x16.c b/media/libeffects/lvm/lib/Common/src/mult3s_16x16.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Common/src/mult3s_16x16.c
rename to media/libeffects/lvm/lib/Common/src/mult3s_16x16.cpp
diff --git a/media/libeffects/lvm/lib/Eq/lib/LVEQNB.h b/media/libeffects/lvm/lib/Eq/lib/LVEQNB.h
index 804f1bf..7e2c3a4 100644
--- a/media/libeffects/lvm/lib/Eq/lib/LVEQNB.h
+++ b/media/libeffects/lvm/lib/Eq/lib/LVEQNB.h
@@ -72,9 +72,6 @@
#ifndef __LVEQNB_H__
#define __LVEQNB_H__
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
/****************************************************************************************/
@@ -492,9 +489,6 @@
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif /* __LVEQNB__ */
diff --git a/media/libeffects/lvm/lib/Eq/src/LVEQNB_CalcCoef.c b/media/libeffects/lvm/lib/Eq/src/LVEQNB_CalcCoef.cpp
similarity index 99%
rename from media/libeffects/lvm/lib/Eq/src/LVEQNB_CalcCoef.c
rename to media/libeffects/lvm/lib/Eq/src/LVEQNB_CalcCoef.cpp
index ff52b7f..482e3ba 100644
--- a/media/libeffects/lvm/lib/Eq/src/LVEQNB_CalcCoef.c
+++ b/media/libeffects/lvm/lib/Eq/src/LVEQNB_CalcCoef.cpp
@@ -359,4 +359,4 @@
return(LVEQNB_SUCCESS);
}
-#endif
\ No newline at end of file
+#endif
diff --git a/media/libeffects/lvm/lib/Eq/src/LVEQNB_Control.c b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Control.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Eq/src/LVEQNB_Control.c
rename to media/libeffects/lvm/lib/Eq/src/LVEQNB_Control.cpp
diff --git a/media/libeffects/lvm/lib/Eq/src/LVEQNB_Init.c b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Init.cpp
similarity index 98%
rename from media/libeffects/lvm/lib/Eq/src/LVEQNB_Init.c
rename to media/libeffects/lvm/lib/Eq/src/LVEQNB_Init.cpp
index de1bbb7..8e3c627 100644
--- a/media/libeffects/lvm/lib/Eq/src/LVEQNB_Init.c
+++ b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Init.cpp
@@ -282,9 +282,9 @@
#ifdef BUILD_FLOAT
/* Equaliser Biquad Instance */
- pInstance->pEQNB_FilterState_Float = InstAlloc_AddMember(&AllocMem,
- pCapabilities->MaxBands * \
- sizeof(Biquad_FLOAT_Instance_t));
+ pInstance->pEQNB_FilterState_Float = (Biquad_FLOAT_Instance_t *)
+ InstAlloc_AddMember(&AllocMem, pCapabilities->MaxBands * \
+ sizeof(Biquad_FLOAT_Instance_t));
#else
pInstance->pEQNB_FilterState = InstAlloc_AddMember(&AllocMem,
pCapabilities->MaxBands * sizeof(Biquad_Instance_t)); /* Equaliser Biquad Instance */
diff --git a/media/libeffects/lvm/lib/Eq/src/LVEQNB_Private.h b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Private.h
index a9cd5fd..4f70eec 100644
--- a/media/libeffects/lvm/lib/Eq/src/LVEQNB_Private.h
+++ b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Private.h
@@ -18,9 +18,6 @@
#ifndef __LVEQNB_PRIVATE_H__
#define __LVEQNB_PRIVATE_H__
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
/****************************************************************************************/
@@ -152,9 +149,6 @@
LVM_INT32 LVEQNB_BypassMixerCallBack (void* hInstance, void *pGeneralPurpose, LVM_INT16 CallbackParam);
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif /* __LVEQNB_PRIVATE_H__ */
diff --git a/media/libeffects/lvm/lib/Eq/src/LVEQNB_Process.c b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Process.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Eq/src/LVEQNB_Process.c
rename to media/libeffects/lvm/lib/Eq/src/LVEQNB_Process.cpp
diff --git a/media/libeffects/lvm/lib/Eq/src/LVEQNB_Tables.c b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Tables.cpp
similarity index 99%
rename from media/libeffects/lvm/lib/Eq/src/LVEQNB_Tables.c
rename to media/libeffects/lvm/lib/Eq/src/LVEQNB_Tables.cpp
index 453c42d..d3d4ba0 100644
--- a/media/libeffects/lvm/lib/Eq/src/LVEQNB_Tables.c
+++ b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Tables.cpp
@@ -24,6 +24,7 @@
#include "LVEQNB.h"
#include "LVEQNB_Coeffs.h"
+#include "LVEQNB_Tables.h"
/************************************************************************************/
diff --git a/media/libeffects/lvm/lib/Eq/src/LVEQNB_Tables.h b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Tables.h
new file mode 100644
index 0000000..dc3fbb6
--- /dev/null
+++ b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Tables.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LVEQNB_TABLES_H__
+#define __LVEQNB_TABLES_H__
+
+/************************************************************************************/
+/* */
+/* Sample rate table */
+/* */
+/************************************************************************************/
+
+/*
+ * Sample rate table for converting between the enumerated type and the actual
+ * frequency
+ */
+#ifdef HIGHER_FS
+extern const LVM_UINT32 LVEQNB_SampleRateTab[];
+#else
+extern const LVM_UINT16 LVEQNB_SampleRateTab[];
+#endif
+
+/************************************************************************************/
+/* */
+/* Coefficient calculation tables */
+/* */
+/************************************************************************************/
+
+/*
+ * Table for 2 * Pi / Fs
+ */
+extern const LVM_FLOAT LVEQNB_TwoPiOnFsTable[];
+
+/*
+ * Gain table
+ */
+extern const LVM_FLOAT LVEQNB_GainTable[];
+
+/*
+ * D table for 100 / (Gain + 1)
+ */
+extern const LVM_FLOAT LVEQNB_DTable[];
+
+/************************************************************************************/
+/* */
+/* Filter polynomial coefficients */
+/* */
+/************************************************************************************/
+
+/*
+ * Coefficients for calculating the cosine with the equation:
+ *
+ * Cos(x) = (2^Shifts)*(a0 + a1*x + a2*x^2 + a3*x^3 + a4*x^4 + a5*x^5)
+ *
+ * These coefficients expect the input, x, to be in the range 0 to 32768 respresenting
+ * a range of 0 to Pi. The output is in the range 32767 to -32768 representing the range
+ * +1.0 to -1.0
+ */
+extern const LVM_INT16 LVEQNB_CosCoef[];
+
+/*
+ * Coefficients for calculating the cosine error with the equation:
+ *
+ * CosErr(x) = (2^Shifts)*(a0 + a1*x + a2*x^2 + a3*x^3)
+ *
+ * These coefficients expect the input, x, to be in the range 0 to 32768 respresenting
+ * a range of 0 to Pi/25. The output is in the range 0 to 32767 representing the range
+ * 0.0 to 0.0078852986
+ *
+ * This is used to give a double precision cosine over the range 0 to Pi/25 using the
+ * the equation:
+ *
+ * Cos(x) = 1.0 - CosErr(x)
+ */
+extern const LVM_INT16 LVEQNB_DPCosCoef[];
+
+#endif /* __LVEQNB_TABLES_H__ */
diff --git a/media/libeffects/lvm/lib/Reverb/lib/LVREV.h b/media/libeffects/lvm/lib/Reverb/lib/LVREV.h
index 9c2e297..4f052b1 100644
--- a/media/libeffects/lvm/lib/Reverb/lib/LVREV.h
+++ b/media/libeffects/lvm/lib/Reverb/lib/LVREV.h
@@ -28,9 +28,6 @@
#ifndef __LVREV_H__
#define __LVREV_H__
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
/****************************************************************************************/
@@ -315,9 +312,6 @@
const LVM_UINT16 NumSamples);
#endif
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif /* __LVREV_H__ */
diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_ApplyNewSettings.c b/media/libeffects/lvm/lib/Reverb/src/LVREV_ApplyNewSettings.cpp
similarity index 99%
rename from media/libeffects/lvm/lib/Reverb/src/LVREV_ApplyNewSettings.c
rename to media/libeffects/lvm/lib/Reverb/src/LVREV_ApplyNewSettings.cpp
index e710844..2c46baa 100644
--- a/media/libeffects/lvm/lib/Reverb/src/LVREV_ApplyNewSettings.c
+++ b/media/libeffects/lvm/lib/Reverb/src/LVREV_ApplyNewSettings.cpp
@@ -638,7 +638,7 @@
FO_1I_D32F32Cll_TRC_WRA_01_Init( &pPrivate->pFastCoef->HPCoefs,
&pPrivate->pFastData->HPTaps, &Coeffs);
LoadConst_Float(0,
- (void *)&pPrivate->pFastData->HPTaps, /* Destination Cast to void: \
+ (LVM_FLOAT *)&pPrivate->pFastData->HPTaps, /* Destination Cast to void: \
no dereferencing in function*/
sizeof(Biquad_1I_Order1_FLOAT_Taps_t) / sizeof(LVM_FLOAT));
}
@@ -672,7 +672,7 @@
FO_1I_D32F32Cll_TRC_WRA_01_Init( &pPrivate->pFastCoef->LPCoefs,
&pPrivate->pFastData->LPTaps, &Coeffs);
LoadConst_Float(0,
- (void *)&pPrivate->pFastData->LPTaps, /* Destination Cast to void: \
+ (LVM_FLOAT *)&pPrivate->pFastData->LPTaps, /* Destination Cast to void: \
no dereferencing in function*/
sizeof(Biquad_1I_Order1_FLOAT_Taps_t) / sizeof(LVM_FLOAT));
}
diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_ClearAudioBuffers.c b/media/libeffects/lvm/lib/Reverb/src/LVREV_ClearAudioBuffers.cpp
similarity index 93%
rename from media/libeffects/lvm/lib/Reverb/src/LVREV_ClearAudioBuffers.c
rename to media/libeffects/lvm/lib/Reverb/src/LVREV_ClearAudioBuffers.cpp
index 9491016..0f41f09 100644
--- a/media/libeffects/lvm/lib/Reverb/src/LVREV_ClearAudioBuffers.c
+++ b/media/libeffects/lvm/lib/Reverb/src/LVREV_ClearAudioBuffers.cpp
@@ -63,13 +63,13 @@
#ifdef BUILD_FLOAT
LoadConst_Float(0,
- (void *)&pLVREV_Private->pFastData->HPTaps, /* Destination Cast to void: \
- no dereferencing in function*/
- 2);
+ (LVM_FLOAT *)&pLVREV_Private->pFastData->HPTaps, /* Destination Cast to void: \
+ no dereferencing in function*/
+ 2);
LoadConst_Float(0,
- (void *)&pLVREV_Private->pFastData->LPTaps, /* Destination Cast to void: \
- no dereferencing in function*/
- 2);
+ (LVM_FLOAT *)&pLVREV_Private->pFastData->LPTaps, /* Destination Cast to void: \
+ no dereferencing in function*/
+ 2);
#else
LoadConst_32(0,
(void *)&pLVREV_Private->pFastData->HPTaps, /* Destination Cast to void: no dereferencing in function*/
diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_GetControlParameters.c b/media/libeffects/lvm/lib/Reverb/src/LVREV_GetControlParameters.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Reverb/src/LVREV_GetControlParameters.c
rename to media/libeffects/lvm/lib/Reverb/src/LVREV_GetControlParameters.cpp
diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_GetInstanceHandle.c b/media/libeffects/lvm/lib/Reverb/src/LVREV_GetInstanceHandle.cpp
similarity index 89%
rename from media/libeffects/lvm/lib/Reverb/src/LVREV_GetInstanceHandle.c
rename to media/libeffects/lvm/lib/Reverb/src/LVREV_GetInstanceHandle.cpp
index 3366bcb..8a27371 100644
--- a/media/libeffects/lvm/lib/Reverb/src/LVREV_GetInstanceHandle.c
+++ b/media/libeffects/lvm/lib/Reverb/src/LVREV_GetInstanceHandle.cpp
@@ -163,7 +163,9 @@
/*
* Set the data, coefficient and temporary memory pointers
*/
- pLVREV_Private->pFastData = InstAlloc_AddMember(&FastData, sizeof(LVREV_FastData_st)); /* Fast data memory base address */
+ /* Fast data memory base address */
+ pLVREV_Private->pFastData = (LVREV_FastData_st *)
+ InstAlloc_AddMember(&FastData, sizeof(LVREV_FastData_st));
#ifndef BUILD_FLOAT
if(pInstanceParams->NumDelays == LVREV_DELAYLINES_4)
{
@@ -211,19 +213,23 @@
#else
if(pInstanceParams->NumDelays == LVREV_DELAYLINES_4)
{
- pLVREV_Private->pDelay_T[3] = InstAlloc_AddMember(&FastData, LVREV_MAX_T3_DELAY * \
+ pLVREV_Private->pDelay_T[3] =
+ (LVM_FLOAT *)InstAlloc_AddMember(&FastData, LVREV_MAX_T3_DELAY * \
sizeof(LVM_FLOAT));
- pLVREV_Private->pDelay_T[2] = InstAlloc_AddMember(&FastData, LVREV_MAX_T2_DELAY * \
+ pLVREV_Private->pDelay_T[2] =
+ (LVM_FLOAT *)InstAlloc_AddMember(&FastData, LVREV_MAX_T2_DELAY * \
sizeof(LVM_FLOAT));
- pLVREV_Private->pDelay_T[1] = InstAlloc_AddMember(&FastData, LVREV_MAX_T1_DELAY * \
+ pLVREV_Private->pDelay_T[1] =
+ (LVM_FLOAT *)InstAlloc_AddMember(&FastData, LVREV_MAX_T1_DELAY * \
sizeof(LVM_FLOAT));
- pLVREV_Private->pDelay_T[0] = InstAlloc_AddMember(&FastData, LVREV_MAX_T0_DELAY * \
+ pLVREV_Private->pDelay_T[0] =
+ (LVM_FLOAT *)InstAlloc_AddMember(&FastData, LVREV_MAX_T0_DELAY * \
sizeof(LVM_FLOAT));
for(i = 0; i < 4; i++)
{
/* Scratch for each delay line output */
- pLVREV_Private->pScratchDelayLine[i] = InstAlloc_AddMember(&Temporary,
+ pLVREV_Private->pScratchDelayLine[i] = (LVM_FLOAT *)InstAlloc_AddMember(&Temporary,
sizeof(LVM_FLOAT) * \
MaxBlockSize);
}
@@ -236,15 +242,17 @@
if(pInstanceParams->NumDelays == LVREV_DELAYLINES_2)
{
- pLVREV_Private->pDelay_T[1] = InstAlloc_AddMember(&FastData, LVREV_MAX_T1_DELAY * \
+ pLVREV_Private->pDelay_T[1] = (LVM_FLOAT *)
+ InstAlloc_AddMember(&FastData, LVREV_MAX_T1_DELAY * \
sizeof(LVM_FLOAT));
- pLVREV_Private->pDelay_T[0] = InstAlloc_AddMember(&FastData, LVREV_MAX_T0_DELAY * \
+ pLVREV_Private->pDelay_T[0] = (LVM_FLOAT *)
+ InstAlloc_AddMember(&FastData, LVREV_MAX_T0_DELAY * \
sizeof(LVM_FLOAT));
for(i = 0; i < 2; i++)
{
/* Scratch for each delay line output */
- pLVREV_Private->pScratchDelayLine[i] = InstAlloc_AddMember(&Temporary,
+ pLVREV_Private->pScratchDelayLine[i] = (LVM_FLOAT *)InstAlloc_AddMember(&Temporary,
sizeof(LVM_FLOAT) * \
MaxBlockSize);
}
@@ -255,13 +263,13 @@
if(pInstanceParams->NumDelays == LVREV_DELAYLINES_1)
{
- pLVREV_Private->pDelay_T[0] = InstAlloc_AddMember(&FastData,
+ pLVREV_Private->pDelay_T[0] = (LVM_FLOAT *)InstAlloc_AddMember(&FastData,
LVREV_MAX_T0_DELAY * sizeof(LVM_FLOAT));
for(i = 0; i < 1; i++)
{
/* Scratch for each delay line output */
- pLVREV_Private->pScratchDelayLine[i] = InstAlloc_AddMember(&Temporary,
+ pLVREV_Private->pScratchDelayLine[i] = (LVM_FLOAT *)InstAlloc_AddMember(&Temporary,
sizeof(LVM_FLOAT) * \
MaxBlockSize);
}
@@ -276,18 +284,25 @@
pLVREV_Private->T[3] = LVREV_MAX_T3_DELAY;
pLVREV_Private->AB_Selection = 1; /* Select smoothing A to B */
-
- pLVREV_Private->pFastCoef = InstAlloc_AddMember(&FastCoef, sizeof(LVREV_FastCoef_st)); /* Fast coefficient memory base address */
+ /* Fast coefficient memory base address */
+ pLVREV_Private->pFastCoef =
+ (LVREV_FastCoef_st *)InstAlloc_AddMember(&FastCoef, sizeof(LVREV_FastCoef_st));
#ifndef BUILD_FLOAT
- pLVREV_Private->pScratch = InstAlloc_AddMember(&Temporary, sizeof(LVM_INT32) * MaxBlockSize); /* General purpose scratch */
- pLVREV_Private->pInputSave = InstAlloc_AddMember(&Temporary, 2 * sizeof(LVM_INT32) * MaxBlockSize); /* Mono->stereo input save for end mix */
+ /* General purpose scratch */
+ pLVREV_Private->pScratch =
+ (LVM_FLOAT *)InstAlloc_AddMember(&Temporary, sizeof(LVM_INT32) * MaxBlockSize);
+ /* Mono->stereo input save for end mix */
+ pLVREV_Private->pInputSave =
+ (LVM_FLOAT *)InstAlloc_AddMember(&Temporary, 2 * sizeof(LVM_INT32) * MaxBlockSize);
LoadConst_32(0, pLVREV_Private->pInputSave, (LVM_INT16)(MaxBlockSize*2));
#else
/* General purpose scratch */
- pLVREV_Private->pScratch = InstAlloc_AddMember(&Temporary, sizeof(LVM_FLOAT) * \
+ pLVREV_Private->pScratch =
+ (LVM_FLOAT *)InstAlloc_AddMember(&Temporary, sizeof(LVM_FLOAT) * \
MaxBlockSize);
/* Mono->stereo input save for end mix */
- pLVREV_Private->pInputSave = InstAlloc_AddMember(&Temporary, 2 * sizeof(LVM_FLOAT) * \
+ pLVREV_Private->pInputSave =
+ (LVM_FLOAT *)InstAlloc_AddMember(&Temporary, 2 * sizeof(LVM_FLOAT) * \
MaxBlockSize);
LoadConst_Float(0, pLVREV_Private->pInputSave, (LVM_INT16)(MaxBlockSize * 2));
#endif
diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_GetMemoryTable.c b/media/libeffects/lvm/lib/Reverb/src/LVREV_GetMemoryTable.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Reverb/src/LVREV_GetMemoryTable.c
rename to media/libeffects/lvm/lib/Reverb/src/LVREV_GetMemoryTable.cpp
diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_Private.h b/media/libeffects/lvm/lib/Reverb/src/LVREV_Private.h
index c915ac0..3379d65 100644
--- a/media/libeffects/lvm/lib/Reverb/src/LVREV_Private.h
+++ b/media/libeffects/lvm/lib/Reverb/src/LVREV_Private.h
@@ -18,9 +18,6 @@
#ifndef __LVREV_PRIVATE_H__
#define __LVREV_PRIVATE_H__
-#ifdef __cplusplus
-extern "C" {
-#endif
/****************************************************************************************/
@@ -309,9 +306,6 @@
LVM_INT16 GeneralPurpose );
-#ifdef __cplusplus
-}
-#endif
#endif /** __LVREV_PRIVATE_H__ **/
diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_Process.c b/media/libeffects/lvm/lib/Reverb/src/LVREV_Process.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Reverb/src/LVREV_Process.c
rename to media/libeffects/lvm/lib/Reverb/src/LVREV_Process.cpp
diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_SetControlParameters.c b/media/libeffects/lvm/lib/Reverb/src/LVREV_SetControlParameters.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/Reverb/src/LVREV_SetControlParameters.c
rename to media/libeffects/lvm/lib/Reverb/src/LVREV_SetControlParameters.cpp
diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_Tables.c b/media/libeffects/lvm/lib/Reverb/src/LVREV_Tables.cpp
similarity index 99%
rename from media/libeffects/lvm/lib/Reverb/src/LVREV_Tables.c
rename to media/libeffects/lvm/lib/Reverb/src/LVREV_Tables.cpp
index 1058740..1ea10a2 100644
--- a/media/libeffects/lvm/lib/Reverb/src/LVREV_Tables.c
+++ b/media/libeffects/lvm/lib/Reverb/src/LVREV_Tables.cpp
@@ -21,6 +21,7 @@
/* */
/****************************************************************************************/
#include "LVREV.h"
+#include "LVREV_Tables.h"
/****************************************************************************************/
/* */
diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_Tables.h b/media/libeffects/lvm/lib/Reverb/src/LVREV_Tables.h
index 0658186..06b534c 100644
--- a/media/libeffects/lvm/lib/Reverb/src/LVREV_Tables.h
+++ b/media/libeffects/lvm/lib/Reverb/src/LVREV_Tables.h
@@ -19,9 +19,6 @@
#ifndef _LVREV_TABLES_H_
#define _LVREV_TABLES_H_
-#ifdef __cplusplus
-extern "C" {
-#endif
/****************************************************************************************/
@@ -48,10 +45,7 @@
#ifndef BUILD_FLOAT
extern LVM_INT32 LVREV_GainPolyTable[24][5];
#else
-extern LVM_FLOAT LVREV_GainPolyTable[24][5];
-#endif
-#ifdef __cplusplus
-}
+extern const LVM_FLOAT LVREV_GainPolyTable[24][5];
#endif
#endif /** _LVREV_TABLES_H_ **/
diff --git a/media/libeffects/lvm/lib/SpectrumAnalyzer/lib/LVPSA.h b/media/libeffects/lvm/lib/SpectrumAnalyzer/lib/LVPSA.h
index 2038fbb..1377655 100644
--- a/media/libeffects/lvm/lib/SpectrumAnalyzer/lib/LVPSA.h
+++ b/media/libeffects/lvm/lib/SpectrumAnalyzer/lib/LVPSA.h
@@ -22,9 +22,6 @@
#include "LVM_Types.h"
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
/****************************************************************************************/
/* */
@@ -289,8 +286,5 @@
LVPSA_InitParams_t *pParams );
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif /* _LVPSA_H */
diff --git a/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Control.c b/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Control.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Control.c
rename to media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Control.cpp
diff --git a/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Init.c b/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Init.cpp
similarity index 82%
rename from media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Init.c
rename to media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Init.cpp
index 1c26860..ff4b275 100644
--- a/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Init.c
+++ b/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Init.cpp
@@ -148,21 +148,29 @@
#ifndef BUILD_FLOAT
pLVPSA_Inst->pPostGains = InstAlloc_AddMember( &Instance, pInitParams->nBands * sizeof(LVM_UINT16) );
#else
- pLVPSA_Inst->pPostGains = InstAlloc_AddMember( &Instance, pInitParams->nBands * \
- sizeof(LVM_FLOAT) );
+ pLVPSA_Inst->pPostGains =
+ (LVM_FLOAT *)InstAlloc_AddMember(&Instance, pInitParams->nBands * sizeof(LVM_FLOAT));
#endif
- pLVPSA_Inst->pFiltersParams = InstAlloc_AddMember( &Instance, pInitParams->nBands * sizeof(LVPSA_FilterParam_t) );
- pLVPSA_Inst->pSpectralDataBufferStart = InstAlloc_AddMember( &Instance, pInitParams->nBands * pLVPSA_Inst->SpectralDataBufferLength * sizeof(LVM_UINT8) );
- pLVPSA_Inst->pPreviousPeaks = InstAlloc_AddMember( &Instance, pInitParams->nBands * sizeof(LVM_UINT8) );
- pLVPSA_Inst->pBPFiltersPrecision = InstAlloc_AddMember( &Instance, pInitParams->nBands * sizeof(LVPSA_BPFilterPrecision_en) );
+ pLVPSA_Inst->pFiltersParams = (LVPSA_FilterParam_t *)
+ InstAlloc_AddMember(&Instance, pInitParams->nBands * sizeof(LVPSA_FilterParam_t));
+ pLVPSA_Inst->pSpectralDataBufferStart = (LVM_UINT8 *)
+ InstAlloc_AddMember(&Instance, pInitParams->nBands * \
+ pLVPSA_Inst->SpectralDataBufferLength * sizeof(LVM_UINT8));
+ pLVPSA_Inst->pPreviousPeaks = (LVM_UINT8 *)
+ InstAlloc_AddMember(&Instance, pInitParams->nBands * sizeof(LVM_UINT8));
+ pLVPSA_Inst->pBPFiltersPrecision = (LVPSA_BPFilterPrecision_en *)
+ InstAlloc_AddMember(&Instance, pInitParams->nBands * \
+ sizeof(LVPSA_BPFilterPrecision_en));
#ifndef BUILD_FLOAT
pLVPSA_Inst->pBP_Instances = InstAlloc_AddMember( &Coef, pInitParams->nBands * sizeof(Biquad_Instance_t) );
pLVPSA_Inst->pQPD_States = InstAlloc_AddMember( &Coef, pInitParams->nBands * sizeof(QPD_State_t) );
#else
- pLVPSA_Inst->pBP_Instances = InstAlloc_AddMember( &Coef, pInitParams->nBands * \
- sizeof(Biquad_FLOAT_Instance_t) );
- pLVPSA_Inst->pQPD_States = InstAlloc_AddMember( &Coef, pInitParams->nBands * \
- sizeof(QPD_FLOAT_State_t) );
+ pLVPSA_Inst->pBP_Instances = (Biquad_FLOAT_Instance_t *)
+ InstAlloc_AddMember(&Coef, pInitParams->nBands * \
+ sizeof(Biquad_FLOAT_Instance_t));
+ pLVPSA_Inst->pQPD_States = (QPD_FLOAT_State_t *)
+ InstAlloc_AddMember(&Coef, pInitParams->nBands * \
+ sizeof(QPD_FLOAT_State_t));
#endif
#ifndef BUILD_FLOAT
@@ -170,11 +178,12 @@
pLVPSA_Inst->pQPD_Taps = InstAlloc_AddMember( &Data, pInitParams->nBands * sizeof(QPD_Taps_t) );
#else
- pLVPSA_Inst->pBP_Taps = InstAlloc_AddMember( &Data,
- pInitParams->nBands * \
- sizeof(Biquad_1I_Order2_FLOAT_Taps_t));
- pLVPSA_Inst->pQPD_Taps = InstAlloc_AddMember( &Data, pInitParams->nBands * \
- sizeof(QPD_FLOAT_Taps_t) );
+ pLVPSA_Inst->pBP_Taps = (Biquad_1I_Order2_FLOAT_Taps_t *)
+ InstAlloc_AddMember(&Data, pInitParams->nBands * \
+ sizeof(Biquad_1I_Order2_FLOAT_Taps_t));
+ pLVPSA_Inst->pQPD_Taps = (QPD_FLOAT_Taps_t *)
+ InstAlloc_AddMember(&Data, pInitParams->nBands * \
+ sizeof(QPD_FLOAT_Taps_t));
#endif
/* Copy filters parameters in the private instance */
diff --git a/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Memory.c b/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Memory.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Memory.c
rename to media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Memory.cpp
diff --git a/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Private.h b/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Private.h
index ee07e2e..bce23c9 100644
--- a/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Private.h
+++ b/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Private.h
@@ -25,9 +25,6 @@
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
/**********************************************************************************
CONSTANT DEFINITIONS
@@ -162,8 +159,5 @@
/************************************************************************************/
LVPSA_RETURN LVPSA_ApplyNewSettings (LVPSA_InstancePr_t *pInst);
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif /* _LVPSA_PRIVATE_H */
diff --git a/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Process.c b/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Process.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Process.c
rename to media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Process.cpp
diff --git a/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_QPD.h b/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_QPD.h
index 99d844b..552703b 100644
--- a/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_QPD.h
+++ b/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_QPD.h
@@ -21,9 +21,6 @@
#include "LVM_Types.h"
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
typedef struct
{
@@ -119,9 +116,6 @@
QPD_FLOAT_Taps_t *pTaps,
QPD_FLOAT_Coefs *pCoef );
#endif
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif
diff --git a/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_QPD_Init.c b/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_QPD_Init.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_QPD_Init.c
rename to media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_QPD_Init.cpp
diff --git a/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_QPD_Process.c b/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_QPD_Process.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_QPD_Process.c
rename to media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_QPD_Process.cpp
diff --git a/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Tables.c b/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Tables.cpp
similarity index 89%
rename from media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Tables.c
rename to media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Tables.cpp
index f8af496..045a502 100644
--- a/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Tables.c
+++ b/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Tables.cpp
@@ -24,6 +24,7 @@
#include "LVPSA.h"
#include "LVPSA_QPD.h"
+#include "LVPSA_Tables.h"
/************************************************************************************/
/* */
/* Sample rate table */
@@ -318,36 +319,38 @@
/* */
/************************************************************************************/
const QPD_C32_Coefs LVPSA_QPD_Coefs[] = {
+ /* 8kS/s */ /* LVPSA_SPEED_LOW */
+ {(LVM_INT32)0x80CEFD2B,0x00CB9B17},
+ {(LVM_INT32)0x80D242E7,0x00CED11D},
+ {(LVM_INT32)0x80DCBAF5,0x00D91679},
+ {(LVM_INT32)0x80CEFD2B,0x00CB9B17},
+ {(LVM_INT32)0x80E13739,0x00DD7CD3},
+ {(LVM_INT32)0x80DCBAF5,0x00D91679},
+ {(LVM_INT32)0x80D94BAF,0x00D5B7E7},
+ {(LVM_INT32)0x80E13739,0x00DD7CD3},
+ {(LVM_INT32)0x80DCBAF5,0x00D91679}, /* 48kS/s */
- {0x80CEFD2B,0x00CB9B17}, /* 8kS/s */ /* LVPSA_SPEED_LOW */
- {0x80D242E7,0x00CED11D},
- {0x80DCBAF5,0x00D91679},
- {0x80CEFD2B,0x00CB9B17},
- {0x80E13739,0x00DD7CD3},
- {0x80DCBAF5,0x00D91679},
- {0x80D94BAF,0x00D5B7E7},
- {0x80E13739,0x00DD7CD3},
- {0x80DCBAF5,0x00D91679}, /* 48kS/s */
+ /* 8kS/s */ /* LVPSA_SPEED_MEDIUM */
+ {(LVM_INT32)0x8587513D,0x055C22CF},
+ {(LVM_INT32)0x859D2967,0x0570F007},
+ {(LVM_INT32)0x85E2EFAC,0x05B34D79},
+ {(LVM_INT32)0x8587513D,0x055C22CF},
+ {(LVM_INT32)0x8600C7B9,0x05CFA6CF},
+ {(LVM_INT32)0x85E2EFAC,0x05B34D79},
+ {(LVM_INT32)0x85CC1018,0x059D8F69},
+ {(LVM_INT32)0x8600C7B9,0x05CFA6CF},
+ {(LVM_INT32)0x85E2EFAC,0x05B34D79}, /* 48kS/s */
- {0x8587513D,0x055C22CF}, /* 8kS/s */ /* LVPSA_SPEED_MEDIUM */
- {0x859D2967,0x0570F007},
- {0x85E2EFAC,0x05B34D79},
- {0x8587513D,0x055C22CF},
- {0x8600C7B9,0x05CFA6CF},
- {0x85E2EFAC,0x05B34D79},
- {0x85CC1018,0x059D8F69},
- {0x8600C7B9,0x05CFA6CF},//{0x8600C7B9,0x05CFA6CF},
- {0x85E2EFAC,0x05B34D79}, /* 48kS/s */
-
- {0xA115EA7A,0x1CDB3F5C}, /* 8kS/s */ /* LVPSA_SPEED_HIGH */
- {0xA18475F0,0x1D2C83A2},
- {0xA2E1E950,0x1E2A532E},
- {0xA115EA7A,0x1CDB3F5C},
- {0xA375B2C6,0x1E943BBC},
- {0xA2E1E950,0x1E2A532E},
- {0xA26FF6BD,0x1DD81530},
- {0xA375B2C6,0x1E943BBC},
- {0xA2E1E950,0x1E2A532E}}; /* 48kS/s */
+ /* 8kS/s */ /* LVPSA_SPEED_HIGH */
+ {(LVM_INT32)0xA115EA7A,0x1CDB3F5C},
+ {(LVM_INT32)0xA18475F0,0x1D2C83A2},
+ {(LVM_INT32)0xA2E1E950,0x1E2A532E},
+ {(LVM_INT32)0xA115EA7A,0x1CDB3F5C},
+ {(LVM_INT32)0xA375B2C6,0x1E943BBC},
+ {(LVM_INT32)0xA2E1E950,0x1E2A532E},
+ {(LVM_INT32)0xA26FF6BD,0x1DD81530},
+ {(LVM_INT32)0xA375B2C6,0x1E943BBC},
+ {(LVM_INT32)0xA2E1E950,0x1E2A532E}}; /* 48kS/s */
#ifdef BUILD_FLOAT
const QPD_FLOAT_Coefs LVPSA_QPD_Float_Coefs[] = {
diff --git a/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Tables.h b/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Tables.h
new file mode 100644
index 0000000..caaf3ba
--- /dev/null
+++ b/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Tables.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LVPSA_TABLES_H__
+#define __LVPSA_TABLES_H__
+
+/************************************************************************************/
+/* */
+/* Sample rate table */
+/* */
+/************************************************************************************/
+
+/*
+ * Sample rate table for converting between the enumerated type and the actual
+ * frequency
+ */
+#ifndef HIGHER_FS
+extern const LVM_UINT16 LVPSA_SampleRateTab[];
+#else
+extern const LVM_UINT32 LVPSA_SampleRateTab[];
+#endif
+
+/************************************************************************************/
+/* */
+/* Sample rate inverse table */
+/* */
+/************************************************************************************/
+
+/*
+ * Sample rate table for converting between the enumerated type and the actual
+ * frequency
+ */
+extern const LVM_UINT32 LVPSA_SampleRateInvTab[];
+
+/************************************************************************************/
+/* */
+/* Number of samples in 20ms */
+/* */
+/************************************************************************************/
+
+/*
+ * Table for converting between the enumerated type and the number of samples
+ * during 20ms
+ */
+extern const LVM_UINT16 LVPSA_nSamplesBufferUpdate[];
+
+/************************************************************************************/
+/* */
+/* Down sampling factors */
+/* */
+/************************************************************************************/
+
+/*
+ * Table for converting between the enumerated type and the down sampling factor
+ */
+extern const LVM_UINT16 LVPSA_DownSamplingFactor[];
+
+/************************************************************************************/
+/* */
+/* Coefficient calculation tables */
+/* */
+/************************************************************************************/
+
+/*
+ * Table for 2 * Pi / Fs
+ */
+extern const LVM_INT16 LVPSA_TwoPiOnFsTable[];
+extern const LVM_FLOAT LVPSA_Float_TwoPiOnFsTable[];
+
+/*
+ * Gain table
+ */
+extern const LVM_INT16 LVPSA_GainTable[];
+extern const LVM_FLOAT LVPSA_Float_GainTable[];
+
+/************************************************************************************/
+/* */
+/* Cosone polynomial coefficients */
+/* */
+/************************************************************************************/
+
+/*
+ * Coefficients for calculating the cosine with the equation:
+ *
+ * Cos(x) = (2^Shifts)*(a0 + a1*x + a2*x^2 + a3*x^3 + a4*x^4 + a5*x^5)
+ *
+ * These coefficients expect the input, x, to be in the range 0 to 32768 respresenting
+ * a range of 0 to Pi. The output is in the range 32767 to -32768 representing the range
+ * +1.0 to -1.0
+ */
+extern const LVM_INT16 LVPSA_CosCoef[];
+extern const LVM_FLOAT LVPSA_Float_CosCoef[];
+
+/*
+ * Coefficients for calculating the cosine error with the equation:
+ *
+ * CosErr(x) = (2^Shifts)*(a0 + a1*x + a2*x^2 + a3*x^3)
+ *
+ * These coefficients expect the input, x, to be in the range 0 to 32768 respresenting
+ * a range of 0 to Pi/25. The output is in the range 0 to 32767 representing the range
+ * 0.0 to 0.0078852986
+ *
+ * This is used to give a double precision cosine over the range 0 to Pi/25 using the
+ * the equation:
+ *
+ * Cos(x) = 1.0 - CosErr(x)
+ */
+extern const LVM_INT16 LVPSA_DPCosCoef[];
+extern const LVM_FLOAT LVPSA_Float_DPCosCoef[];
+
+/************************************************************************************/
+/* */
+/* Quasi peak filter coefficients table */
+/* */
+/************************************************************************************/
+extern const QPD_C32_Coefs LVPSA_QPD_Coefs[];
+extern const QPD_FLOAT_Coefs LVPSA_QPD_Float_Coefs[];
+
+#endif /* __LVPSA_TABLES_H__ */
diff --git a/media/libeffects/lvm/lib/StereoWidening/lib/LVCS.h b/media/libeffects/lvm/lib/StereoWidening/lib/LVCS.h
index e507a7c..174c86a 100644
--- a/media/libeffects/lvm/lib/StereoWidening/lib/LVCS.h
+++ b/media/libeffects/lvm/lib/StereoWidening/lib/LVCS.h
@@ -56,9 +56,6 @@
#ifndef LVCS_H
#define LVCS_H
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
/****************************************************************************************/
@@ -389,8 +386,5 @@
LVM_UINT16 NumSamples);
#endif
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif /* LVCS_H */
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_BypassMix.c b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_BypassMix.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/StereoWidening/src/LVCS_BypassMix.c
rename to media/libeffects/lvm/lib/StereoWidening/src/LVCS_BypassMix.cpp
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_BypassMix.h b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_BypassMix.h
index f69ba38..6ec2ac5 100644
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_BypassMix.h
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_BypassMix.h
@@ -18,9 +18,6 @@
#ifndef __LVCS_BYPASSMIX_H__
#define __LVCS_BYPASSMIX_H__
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
/************************************************************************************/
@@ -91,8 +88,5 @@
LVM_FLOAT *pOutData,
LVM_UINT16 NumSamples);
#endif
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif /* BYPASSMIX_H */
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Control.c b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Control.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/StereoWidening/src/LVCS_Control.c
rename to media/libeffects/lvm/lib/StereoWidening/src/LVCS_Control.cpp
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Equaliser.c b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Equaliser.cpp
similarity index 98%
rename from media/libeffects/lvm/lib/StereoWidening/src/LVCS_Equaliser.c
rename to media/libeffects/lvm/lib/StereoWidening/src/LVCS_Equaliser.cpp
index ec5312e..cd53a11 100644
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Equaliser.c
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Equaliser.cpp
@@ -92,7 +92,7 @@
Coeffs.B2 = (LVM_FLOAT)-pEqualiserCoefTable[Offset].B2;
LoadConst_Float((LVM_INT16)0, /* Value */
- (void *)&pData->EqualiserBiquadTaps, /* Destination Cast to void:\
+ (LVM_FLOAT *)&pData->EqualiserBiquadTaps, /* Destination Cast to void:\
no dereferencing in function*/
/* Number of words */
(LVM_UINT16)(sizeof(pData->EqualiserBiquadTaps) / sizeof(LVM_FLOAT)));
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Equaliser.h b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Equaliser.h
index 0e756e7..55c4815 100644
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Equaliser.h
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Equaliser.h
@@ -18,9 +18,6 @@
#ifndef __LVCS_EQUALISER_H__
#define __LVCS_EQUALISER_H__
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
/************************************************************************************/
@@ -57,8 +54,5 @@
LVM_FLOAT *pInputOutput,
LVM_UINT16 NumSamples);
#endif
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif /* EQUALISER_H */
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Init.c b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Init.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/StereoWidening/src/LVCS_Init.c
rename to media/libeffects/lvm/lib/StereoWidening/src/LVCS_Init.cpp
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Private.h b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Private.h
index c8df8e4..513758d 100644
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Private.h
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Private.h
@@ -27,9 +27,6 @@
#ifndef __LVCS_PRIVATE_H__
#define __LVCS_PRIVATE_H__
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
/************************************************************************************/
@@ -193,9 +190,6 @@
LVM_INT32 CallbackParam);
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif /* PRIVATE_H */
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Process.c b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Process.cpp
similarity index 100%
rename from media/libeffects/lvm/lib/StereoWidening/src/LVCS_Process.c
rename to media/libeffects/lvm/lib/StereoWidening/src/LVCS_Process.cpp
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_ReverbGenerator.c b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_ReverbGenerator.cpp
similarity index 98%
rename from media/libeffects/lvm/lib/StereoWidening/src/LVCS_ReverbGenerator.c
rename to media/libeffects/lvm/lib/StereoWidening/src/LVCS_ReverbGenerator.cpp
index 1085101..fbdf57b 100644
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_ReverbGenerator.c
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_ReverbGenerator.cpp
@@ -112,7 +112,7 @@
Coeffs.B2 = (LVM_FLOAT)-pReverbCoefTable[Offset].B2;
LoadConst_Float(0, /* Value */
- (void *)&pData->ReverbBiquadTaps, /* Destination Cast to void:
+ (LVM_FLOAT *)&pData->ReverbBiquadTaps, /* Destination Cast to void:
no dereferencing in function*/
/* Number of words */
(LVM_UINT16)(sizeof(pData->ReverbBiquadTaps) / sizeof(LVM_FLOAT)));
@@ -306,7 +306,7 @@
* Check if the reverb is required
*/
/* Disable when CS4MS in stereo mode */
- if (((pInstance->Params.SpeakerType == LVCS_HEADPHONE) || \
+ if ((((LVCS_OutputDevice_en)pInstance->Params.SpeakerType == LVCS_HEADPHONE) || \
(pInstance->Params.SpeakerType == LVCS_EX_HEADPHONES) ||
(pInstance->Params.SourceFormat != LVCS_STEREO)) &&
/* For validation testing */
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_ReverbGenerator.h b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_ReverbGenerator.h
index f94d4e4..c1c0207 100644
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_ReverbGenerator.h
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_ReverbGenerator.h
@@ -18,9 +18,6 @@
#ifndef __LVCS_REVERBGENERATOR_H__
#define __LVCS_REVERBGENERATOR_H__
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
/************************************************************************************/
@@ -94,8 +91,5 @@
LVM_INT16 *pOutput,
LVM_UINT16 NumSamples);
#endif
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif /* REVERB_H */
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_StereoEnhancer.c b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_StereoEnhancer.cpp
similarity index 98%
rename from media/libeffects/lvm/lib/StereoWidening/src/LVCS_StereoEnhancer.c
rename to media/libeffects/lvm/lib/StereoWidening/src/LVCS_StereoEnhancer.cpp
index 2992c35..f73fc28 100644
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_StereoEnhancer.c
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_StereoEnhancer.cpp
@@ -89,7 +89,7 @@
/* Clear the taps */
LoadConst_Float(0, /* Value */
- (void *)&pData->SEBiquadTapsMid, /* Destination Cast to void:\
+ (LVM_FLOAT *)&pData->SEBiquadTapsMid, /* Destination Cast to void:\
no dereferencing in function*/
/* Number of words */
(LVM_UINT16)(sizeof(pData->SEBiquadTapsMid) / sizeof(LVM_FLOAT)));
@@ -117,7 +117,7 @@
/* Clear the taps */
LoadConst_Float(0, /* Value */
- (void *)&pData->SEBiquadTapsSide, /* Destination Cast to void:\
+ (LVM_FLOAT *)&pData->SEBiquadTapsSide, /* Destination Cast to void:\
no dereferencing in function*/
/* Number of words */
(LVM_UINT16)(sizeof(pData->SEBiquadTapsSide) / sizeof(LVM_FLOAT)));
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_StereoEnhancer.h b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_StereoEnhancer.h
index 4125f24..79ebb67 100644
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_StereoEnhancer.h
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_StereoEnhancer.h
@@ -18,9 +18,6 @@
#ifndef __LVCS_STEREOENHANCER_H__
#define __LVCS_STEREOENHANCER_H__
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
/************************************************************************************/
@@ -91,8 +88,5 @@
LVM_FLOAT *pOutData,
LVM_UINT16 NumSamples);
#endif
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif /* STEREOENHANCE_H */
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Tables.c b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Tables.cpp
similarity index 99%
rename from media/libeffects/lvm/lib/StereoWidening/src/LVCS_Tables.c
rename to media/libeffects/lvm/lib/StereoWidening/src/LVCS_Tables.cpp
index a1fb48f..1964c8c 100644
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Tables.c
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Tables.cpp
@@ -23,6 +23,7 @@
/************************************************************************************/
#include "LVCS_Private.h"
+#include "LVCS_Tables.h"
#include "Filters.h" /* Filter definitions */
#include "BIQUAD.h" /* Biquad definitions */
#include "LVCS_Headphone_Coeffs.h" /* Headphone coefficients */
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Tables.h b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Tables.h
index 3f6c4c8..8609ad6 100644
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Tables.h
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Tables.h
@@ -18,9 +18,6 @@
#ifndef __LVCS_TABLES_H__
#define __LVCS_TABLES_H__
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
/************************************************************************************/
/* */
@@ -111,7 +108,7 @@
/* */
/************************************************************************************/
-extern LVM_INT32 LVCS_SampleRateTable[];
+extern const LVM_INT32 LVCS_SampleRateTable[];
/*Speaker coeffient tables*/
@@ -144,9 +141,6 @@
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
#endif /* __LVCS_TABLES_H__ */
diff --git a/media/libeffects/lvm/wrapper/Android.bp b/media/libeffects/lvm/wrapper/Android.bp
index 16fa126..5fb6d12 100644
--- a/media/libeffects/lvm/wrapper/Android.bp
+++ b/media/libeffects/lvm/wrapper/Android.bp
@@ -14,7 +14,7 @@
vendor: true,
srcs: ["Bundle/EffectBundle.cpp"],
- cflags: [
+ cppflags: [
"-fvisibility=hidden",
"-DBUILD_FLOAT",
"-DHIGHER_FS",
@@ -56,7 +56,7 @@
vendor: true,
srcs: ["Reverb/EffectReverb.cpp"],
- cflags: [
+ cppflags: [
"-fvisibility=hidden",
"-DBUILD_FLOAT",
"-DHIGHER_FS",
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
index e4aacd0..d8e2ec6 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
@@ -23,9 +23,6 @@
#include <LVM.h>
#include <limits.h>
-#if __cplusplus
-extern "C" {
-#endif
#define FIVEBAND_NUMBANDS 5
#define MAX_NUM_BANDS 5
@@ -228,9 +225,6 @@
static const float LimitLevel_virtualizerContribution = 1.9;
-#if __cplusplus
-} // extern "C"
-#endif
#endif /*ANDROID_EFFECTBUNDLE_H_*/
diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.h b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.h
index 8165f5a..b2d47af 100644
--- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.h
+++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.h
@@ -20,9 +20,6 @@
#include <audio_effects/effect_environmentalreverb.h>
#include <audio_effects/effect_presetreverb.h>
-#if __cplusplus
-extern "C" {
-#endif
#define MAX_NUM_BANDS 5
#define MAX_CALL_SIZE 256
@@ -38,9 +35,6 @@
int16_t Room_HF;
int16_t LPF;
} LPFPair_t;
-#if __cplusplus
-} // extern "C"
-#endif
#endif /*ANDROID_EFFECTREVERB_H_*/
diff --git a/media/libheif/HeifDecoderImpl.cpp b/media/libheif/HeifDecoderImpl.cpp
index 9d79f89..273d91c 100644
--- a/media/libheif/HeifDecoderImpl.cpp
+++ b/media/libheif/HeifDecoderImpl.cpp
@@ -26,7 +26,7 @@
#include <binder/MemoryDealer.h>
#include <drm/drm_framework_common.h>
#include <media/mediametadataretriever.h>
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/stagefright/foundation/ADebug.h>
#include <private/media/VideoFrame.h>
#include <utils/Log.h>
@@ -334,6 +334,13 @@
}
mDataSource = dataSource;
+ return reinit(frameInfo);
+}
+
+bool HeifDecoderImpl::reinit(HeifFrameInfo* frameInfo) {
+ mFrameDecoded = false;
+ mFrameMemory.clear();
+
mRetriever = new MediaMetadataRetriever();
status_t err = mRetriever->setDataSource(mDataSource, "image/heif");
if (err != OK) {
@@ -457,27 +464,35 @@
}
bool HeifDecoderImpl::setOutputColor(HeifColorFormat heifColor) {
+ if (heifColor == mOutputColor) {
+ return true;
+ }
+
switch(heifColor) {
case kHeifColorFormat_RGB565:
{
mOutputColor = HAL_PIXEL_FORMAT_RGB_565;
- return true;
+ break;
}
case kHeifColorFormat_RGBA_8888:
{
mOutputColor = HAL_PIXEL_FORMAT_RGBA_8888;
- return true;
+ break;
}
case kHeifColorFormat_BGRA_8888:
{
mOutputColor = HAL_PIXEL_FORMAT_BGRA_8888;
- return true;
+ break;
}
default:
- break;
+ ALOGE("Unsupported output color format %d", heifColor);
+ return false;
}
- ALOGE("Unsupported output color format %d", heifColor);
- return false;
+
+ if (mFrameDecoded) {
+ return reinit(nullptr);
+ }
+ return true;
}
bool HeifDecoderImpl::decodeAsync() {
@@ -506,7 +521,8 @@
}
// Aggressive clear to avoid holding on to resources
mRetriever.clear();
- mDataSource.clear();
+
+ // Hold on to mDataSource in case the client wants to redecode.
return false;
}
@@ -606,7 +622,8 @@
// Aggressively clear to avoid holding on to resources
mRetriever.clear();
- mDataSource.clear();
+
+ // Hold on to mDataSource in case the client wants to redecode.
return true;
}
diff --git a/media/libheif/HeifDecoderImpl.h b/media/libheif/HeifDecoderImpl.h
index 69c74a7..2b9c710 100644
--- a/media/libheif/HeifDecoderImpl.h
+++ b/media/libheif/HeifDecoderImpl.h
@@ -81,6 +81,7 @@
bool decodeAsync();
bool getScanlineInner(uint8_t* dst);
+ bool reinit(HeifFrameInfo* frameInfo);
};
} // namespace android
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index e8ff463..867187d 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -42,7 +42,6 @@
"aidl/android/media/MediaResourceParcel.aidl",
"aidl/android/media/MediaResourcePolicyParcel.aidl",
],
- versions: ["1"],
}
cc_library_shared {
diff --git a/media/libmedia/IMediaExtractor.cpp b/media/libmedia/IMediaExtractor.cpp
index 7389851..39caf53 100644
--- a/media/libmedia/IMediaExtractor.cpp
+++ b/media/libmedia/IMediaExtractor.cpp
@@ -107,8 +107,15 @@
}
virtual uint32_t flags() const {
- ALOGV("flags NOT IMPLEMENTED");
- return 0;
+ ALOGV("flags");
+ Parcel data, reply;
+ data.writeInterfaceToken(BpMediaExtractor::getInterfaceDescriptor());
+ status_t ret = remote()->transact(FLAGS, data, &reply);
+ int flgs = 0;
+ if (ret == NO_ERROR) {
+ flgs = reply.readUint32();
+ }
+ return flgs;
}
virtual status_t setMediaCas(const HInterfaceToken &casToken) {
@@ -125,9 +132,15 @@
return reply.readInt32();
}
- virtual const char * name() {
- ALOGV("name NOT IMPLEMENTED");
- return NULL;
+ virtual String8 name() {
+ Parcel data, reply;
+ data.writeInterfaceToken(BpMediaExtractor::getInterfaceDescriptor());
+ status_t ret = remote()->transact(NAME, data, &reply);
+ String8 nm;
+ if (ret == NO_ERROR) {
+ nm = reply.readString8();
+ }
+ return nm;
}
};
@@ -192,6 +205,12 @@
status_t ret = getMetrics(reply);
return ret;
}
+ case FLAGS: {
+ ALOGV("flags");
+ CHECK_INTERFACE(IMediaExtractor, data, reply);
+ reply->writeUint32(this->flags());
+ return NO_ERROR;
+ }
case SETMEDIACAS: {
ALOGV("setMediaCas");
CHECK_INTERFACE(IMediaExtractor, data, reply);
@@ -206,6 +225,13 @@
reply->writeInt32(setMediaCas(casToken));
return OK;
}
+ case NAME: {
+ ALOGV("name");
+ CHECK_INTERFACE(IMediaExtractor, data, reply);
+ String8 nm = name();
+ reply->writeString8(nm);
+ return NO_ERROR;
+ }
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libmedia/IMediaSource.cpp b/media/libmedia/IMediaSource.cpp
index 50826c5..b18571f 100644
--- a/media/libmedia/IMediaSource.cpp
+++ b/media/libmedia/IMediaSource.cpp
@@ -26,7 +26,7 @@
#include <media/IMediaSource.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaBufferGroup.h>
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MetaData.h>
namespace android {
diff --git a/media/libmedia/MidiIoWrapper.cpp b/media/libmedia/MidiIoWrapper.cpp
index 6d46363..e71ea2c 100644
--- a/media/libmedia/MidiIoWrapper.cpp
+++ b/media/libmedia/MidiIoWrapper.cpp
@@ -49,7 +49,7 @@
mDataSource = nullptr;
}
-class DataSourceUnwrapper {
+class MidiIoWrapper::DataSourceUnwrapper {
public:
explicit DataSourceUnwrapper(CDataSource *csource) {
diff --git a/media/libmedia/aidl_api/resourcemanager_aidl_interface/.hash b/media/libmedia/aidl_api/resourcemanager_aidl_interface/.hash
deleted file mode 100644
index e56d56b..0000000
--- a/media/libmedia/aidl_api/resourcemanager_aidl_interface/.hash
+++ /dev/null
@@ -1,18 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-58fe4b26909c9c4f17b1803baa4005c10ee40750 -
diff --git a/media/libmedia/aidl_api/resourcemanager_aidl_interface/1/android/media/IResourceManagerClient.aidl b/media/libmedia/aidl_api/resourcemanager_aidl_interface/1/android/media/IResourceManagerClient.aidl
deleted file mode 100644
index 20bfe72..0000000
--- a/media/libmedia/aidl_api/resourcemanager_aidl_interface/1/android/media/IResourceManagerClient.aidl
+++ /dev/null
@@ -1,22 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.media;
-interface IResourceManagerClient {
- boolean reclaimResource();
- @utf8InCpp String getName();
-}
diff --git a/media/libmedia/aidl_api/resourcemanager_aidl_interface/1/android/media/IResourceManagerService.aidl b/media/libmedia/aidl_api/resourcemanager_aidl_interface/1/android/media/IResourceManagerService.aidl
deleted file mode 100644
index 53cf036..0000000
--- a/media/libmedia/aidl_api/resourcemanager_aidl_interface/1/android/media/IResourceManagerService.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.media;
-interface IResourceManagerService {
- void config(in android.media.MediaResourcePolicyParcel[] policies);
- void addResource(int pid, int uid, long clientId, android.media.IResourceManagerClient client, in android.media.MediaResourceParcel[] resources);
- void removeResource(int pid, long clientId, in android.media.MediaResourceParcel[] resources);
- void removeClient(int pid, long clientId);
- boolean reclaimResource(int pid, in android.media.MediaResourceParcel[] resources);
- const String kPolicySupportsMultipleSecureCodecs = "supports-multiple-secure-codecs";
- const String kPolicySupportsSecureWithNonSecureCodec = "supports-secure-with-non-secure-codec";
-}
diff --git a/media/libmedia/aidl_api/resourcemanager_aidl_interface/1/android/media/MediaResourceParcel.aidl b/media/libmedia/aidl_api/resourcemanager_aidl_interface/1/android/media/MediaResourceParcel.aidl
deleted file mode 100644
index 47ea9bc..0000000
--- a/media/libmedia/aidl_api/resourcemanager_aidl_interface/1/android/media/MediaResourceParcel.aidl
+++ /dev/null
@@ -1,24 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.media;
-parcelable MediaResourceParcel {
- android.media.MediaResourceType type;
- android.media.MediaResourceSubType subType;
- byte[] id;
- long value = 0;
-}
diff --git a/media/libmedia/aidl_api/resourcemanager_aidl_interface/1/android/media/MediaResourcePolicyParcel.aidl b/media/libmedia/aidl_api/resourcemanager_aidl_interface/1/android/media/MediaResourcePolicyParcel.aidl
deleted file mode 100644
index 85d2588..0000000
--- a/media/libmedia/aidl_api/resourcemanager_aidl_interface/1/android/media/MediaResourcePolicyParcel.aidl
+++ /dev/null
@@ -1,22 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.media;
-parcelable MediaResourcePolicyParcel {
- @utf8InCpp String type;
- @utf8InCpp String value;
-}
diff --git a/media/libmedia/aidl_api/resourcemanager_aidl_interface/1/android/media/MediaResourceSubType.aidl b/media/libmedia/aidl_api/resourcemanager_aidl_interface/1/android/media/MediaResourceSubType.aidl
deleted file mode 100644
index 19b68af..0000000
--- a/media/libmedia/aidl_api/resourcemanager_aidl_interface/1/android/media/MediaResourceSubType.aidl
+++ /dev/null
@@ -1,24 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.media;
-@Backing(type="int")
-enum MediaResourceSubType {
- kUnspecifiedSubType = 0,
- kAudioCodec = 1,
- kVideoCodec = 2,
-}
diff --git a/media/libmedia/aidl_api/resourcemanager_aidl_interface/1/android/media/MediaResourceType.aidl b/media/libmedia/aidl_api/resourcemanager_aidl_interface/1/android/media/MediaResourceType.aidl
deleted file mode 100644
index 6a123fc..0000000
--- a/media/libmedia/aidl_api/resourcemanager_aidl_interface/1/android/media/MediaResourceType.aidl
+++ /dev/null
@@ -1,28 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.media;
-@Backing(type="int")
-enum MediaResourceType {
- kUnspecified = 0,
- kSecureCodec = 1,
- kNonSecureCodec = 2,
- kGraphicMemory = 3,
- kCpuBoost = 4,
- kBattery = 5,
- kDrmSession = 6,
-}
diff --git a/media/libmedia/include/android/IMediaExtractor.h b/media/libmedia/include/android/IMediaExtractor.h
index 75e4ee2..3e035ad 100644
--- a/media/libmedia/include/android/IMediaExtractor.h
+++ b/media/libmedia/include/android/IMediaExtractor.h
@@ -62,7 +62,7 @@
virtual status_t setMediaCas(const HInterfaceToken &casToken) = 0;
- virtual const char * name() = 0;
+ virtual String8 name() = 0;
};
diff --git a/media/libmedia/include/media/CounterMetric.h b/media/libmedia/include/media/CounterMetric.h
index b53470d..8bd4049 100644
--- a/media/libmedia/include/media/CounterMetric.h
+++ b/media/libmedia/include/media/CounterMetric.h
@@ -20,7 +20,7 @@
#include <map>
#include <string>
-#include <media/MediaAnalyticsItem.h>
+#include <media/MediaMetricsItem.h>
#include <utils/Log.h>
namespace android {
diff --git a/media/libmedia/include/media/EventMetric.h b/media/libmedia/include/media/EventMetric.h
index dbb736a..d6f3402 100644
--- a/media/libmedia/include/media/EventMetric.h
+++ b/media/libmedia/include/media/EventMetric.h
@@ -16,7 +16,7 @@
#ifndef ANDROID_EVENT_METRIC_H_
#define ANDROID_EVENT_METRIC_H_
-#include <media/MediaAnalyticsItem.h>
+#include <media/MediaMetricsItem.h>
#include <utils/Timers.h>
namespace android {
diff --git a/media/libmedia/include/media/IMediaPlayer.h b/media/libmedia/include/media/IMediaPlayer.h
index 97a998e..3c13540 100644
--- a/media/libmedia/include/media/IMediaPlayer.h
+++ b/media/libmedia/include/media/IMediaPlayer.h
@@ -23,7 +23,7 @@
#include <utils/KeyedVector.h>
#include <system/audio.h>
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/VolumeShaper.h>
// Fwd decl to make sure everyone agrees that the scope of struct sockaddr_in is
diff --git a/media/libmedia/include/media/IMediaSource.h b/media/libmedia/include/media/IMediaSource.h
index 381df24..f3fa39b 100644
--- a/media/libmedia/include/media/IMediaSource.h
+++ b/media/libmedia/include/media/IMediaSource.h
@@ -22,7 +22,7 @@
#include <binder/IInterface.h>
#include <binder/IMemory.h>
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaErrors.h>
diff --git a/media/libmedia/include/media/MediaCodecBuffer.h b/media/libmedia/include/media/MediaCodecBuffer.h
index 2c16fba..101377c 100644
--- a/media/libmedia/include/media/MediaCodecBuffer.h
+++ b/media/libmedia/include/media/MediaCodecBuffer.h
@@ -22,6 +22,8 @@
#include <utils/RefBase.h>
#include <utils/StrongPointer.h>
+class C2Buffer;
+
namespace android {
struct ABuffer;
@@ -57,6 +59,36 @@
void setFormat(const sp<AMessage> &format);
+ /**
+ * \return C2Buffer object represents this buffer.
+ */
+ virtual std::shared_ptr<C2Buffer> asC2Buffer() { return nullptr; }
+
+ /**
+ * Test if we can copy the content of |buffer| into this object.
+ *
+ * \param buffer C2Buffer object to copy.
+ * \return true if the content of buffer can be copied over to this buffer
+ * false otherwise.
+ */
+ virtual bool canCopy(const std::shared_ptr<C2Buffer> &buffer) const {
+ (void)buffer;
+ return false;
+ }
+
+ /**
+ * Copy the content of |buffer| into this object. This method assumes that
+ * canCopy() check already passed.
+ *
+ * \param buffer C2Buffer object to copy.
+ * \return true if successful
+ * false otherwise.
+ */
+ virtual bool copy(const std::shared_ptr<C2Buffer> &buffer) {
+ (void)buffer;
+ return false;
+ }
+
private:
MediaCodecBuffer() = delete;
diff --git a/media/libmedia/include/media/MidiIoWrapper.h b/media/libmedia/include/media/MidiIoWrapper.h
index b19d49e..0cdd4ad 100644
--- a/media/libmedia/include/media/MidiIoWrapper.h
+++ b/media/libmedia/include/media/MidiIoWrapper.h
@@ -19,12 +19,9 @@
#include <libsonivox/eas_types.h>
-#include <media/DataSourceBase.h>
-
namespace android {
struct CDataSource;
-class DataSourceUnwrapper;
class MidiIoWrapper {
public:
@@ -43,6 +40,7 @@
int mFd;
off64_t mBase;
int64_t mLength;
+ class DataSourceUnwrapper;
DataSourceUnwrapper *mDataSource;
EAS_FILE mEasFile;
};
diff --git a/media/libmedia/include/media/NdkWrapper.h b/media/libmedia/include/media/NdkWrapper.h
index 8a417f6..39c0574 100644
--- a/media/libmedia/include/media/NdkWrapper.h
+++ b/media/libmedia/include/media/NdkWrapper.h
@@ -19,7 +19,7 @@
#define NDK_WRAPPER_H_
#include <media/DataSource.h>
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/NdkMediaDataSource.h>
#include <media/NdkMediaError.h>
#include <media/NdkMediaExtractor.h>
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index 60d2f85..1fadc94 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -60,7 +60,7 @@
mVideoWidth = mVideoHeight = 0;
mLockThreadId = 0;
mAudioSessionId = (audio_session_t) AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
- AudioSystem::acquireAudioSessionId(mAudioSessionId, -1);
+ AudioSystem::acquireAudioSessionId(mAudioSessionId, (pid_t)-1, (uid_t)-1); // always in client.
mSendLevel = 0;
mRetransmitEndpointValid = false;
}
@@ -72,7 +72,7 @@
delete mAudioAttributesParcel;
mAudioAttributesParcel = NULL;
}
- AudioSystem::releaseAudioSessionId(mAudioSessionId, -1);
+ AudioSystem::releaseAudioSessionId(mAudioSessionId, (pid_t)-1);
disconnect();
IPCThreadState::self()->flushCommands();
}
@@ -709,8 +709,8 @@
return BAD_VALUE;
}
if (sessionId != mAudioSessionId) {
- AudioSystem::acquireAudioSessionId(sessionId, -1);
- AudioSystem::releaseAudioSessionId(mAudioSessionId, -1);
+ AudioSystem::acquireAudioSessionId(sessionId, (pid_t)-1, (uid_t)-1);
+ AudioSystem::releaseAudioSessionId(mAudioSessionId, (pid_t)-1);
mAudioSessionId = sessionId;
}
return NO_ERROR;
diff --git a/media/libmediahelper/TypeConverter.cpp b/media/libmediahelper/TypeConverter.cpp
index aa54b82..d300c7a 100644
--- a/media/libmediahelper/TypeConverter.cpp
+++ b/media/libmediahelper/TypeConverter.cpp
@@ -312,6 +312,7 @@
MAKE_STRING_FROM_ENUM(AUDIO_STREAM_DTMF),
MAKE_STRING_FROM_ENUM(AUDIO_STREAM_TTS),
MAKE_STRING_FROM_ENUM(AUDIO_STREAM_ACCESSIBILITY),
+ MAKE_STRING_FROM_ENUM(AUDIO_STREAM_ASSISTANT),
MAKE_STRING_FROM_ENUM(AUDIO_STREAM_REROUTING),
MAKE_STRING_FROM_ENUM(AUDIO_STREAM_PATCH),
TERMINATOR
@@ -359,6 +360,10 @@
MAKE_STRING_FROM_ENUM(AUDIO_USAGE_VIRTUAL_SOURCE),
MAKE_STRING_FROM_ENUM(AUDIO_USAGE_ASSISTANT),
MAKE_STRING_FROM_ENUM(AUDIO_USAGE_CALL_ASSISTANT),
+ MAKE_STRING_FROM_ENUM(AUDIO_USAGE_EMERGENCY),
+ MAKE_STRING_FROM_ENUM(AUDIO_USAGE_SAFETY),
+ MAKE_STRING_FROM_ENUM(AUDIO_USAGE_VEHICLE_STATUS),
+ MAKE_STRING_FROM_ENUM(AUDIO_USAGE_ANNOUNCEMENT),
TERMINATOR
};
@@ -395,6 +400,7 @@
MAKE_STRING_FROM_ENUM(AUDIO_FLAG_LOW_LATENCY),
MAKE_STRING_FROM_ENUM(AUDIO_FLAG_DEEP_BUFFER),
MAKE_STRING_FROM_ENUM(AUDIO_FLAG_NO_MEDIA_PROJECTION),
+ MAKE_STRING_FROM_ENUM(AUDIO_FLAG_MUTE_HAPTIC),
MAKE_STRING_FROM_ENUM(AUDIO_FLAG_NO_SYSTEM_CAPTURE),
MAKE_STRING_FROM_ENUM(AUDIO_FLAG_CAPTURE_PRIVATE),
TERMINATOR
diff --git a/media/libmediametrics/Android.bp b/media/libmediametrics/Android.bp
index f599190..0cd5194 100644
--- a/media/libmediametrics/Android.bp
+++ b/media/libmediametrics/Android.bp
@@ -1,9 +1,14 @@
+cc_library_headers {
+ name: "libmediametrics_headers",
+ export_include_dirs: ["include"],
+}
+
cc_library_shared {
name: "libmediametrics",
srcs: [
- "IMediaAnalyticsService.cpp",
- "MediaAnalyticsItem.cpp",
+ "IMediaMetricsService.cpp",
+ "MediaMetricsItem.cpp",
"MediaMetrics.cpp",
],
diff --git a/media/libmediametrics/IMediaAnalyticsService.cpp b/media/libmediametrics/IMediaMetricsService.cpp
similarity index 80%
rename from media/libmediametrics/IMediaAnalyticsService.cpp
rename to media/libmediametrics/IMediaMetricsService.cpp
index 534aece..b5675e6 100644
--- a/media/libmediametrics/IMediaAnalyticsService.cpp
+++ b/media/libmediametrics/IMediaMetricsService.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "MediaAnalytics"
+#define LOG_TAG "MediaMetrics"
#include <stdint.h>
#include <inttypes.h>
@@ -29,8 +29,8 @@
#include <utils/Log.h>
#include <utils/String8.h>
-#include <media/MediaAnalyticsItem.h>
-#include <media/IMediaAnalyticsService.h>
+#include <media/MediaMetricsItem.h>
+#include <media/IMediaMetricsService.h>
namespace android {
@@ -41,15 +41,15 @@
SUBMIT_BUFFER,
};
-class BpMediaAnalyticsService: public BpInterface<IMediaAnalyticsService>
+class BpMediaMetricsService: public BpInterface<IMediaMetricsService>
{
public:
- explicit BpMediaAnalyticsService(const sp<IBinder>& impl)
- : BpInterface<IMediaAnalyticsService>(impl)
+ explicit BpMediaMetricsService(const sp<IBinder>& impl)
+ : BpInterface<IMediaMetricsService>(impl)
{
}
- status_t submit(MediaAnalyticsItem *item) override
+ status_t submit(mediametrics::Item *item) override
{
if (item == nullptr) {
return BAD_VALUE;
@@ -57,7 +57,7 @@
ALOGV("%s: (ONEWAY) item=%s", __func__, item->toString().c_str());
Parcel data;
- data.writeInterfaceToken(IMediaAnalyticsService::getInterfaceDescriptor());
+ data.writeInterfaceToken(IMediaMetricsService::getInterfaceDescriptor());
status_t status = item->writeToParcel(&data);
if (status != NO_ERROR) { // assume failure logged in item
@@ -79,7 +79,7 @@
ALOGV("%s: (ONEWAY) length:%zu", __func__, length);
Parcel data;
- data.writeInterfaceToken(IMediaAnalyticsService::getInterfaceDescriptor());
+ data.writeInterfaceToken(IMediaMetricsService::getInterfaceDescriptor());
status_t status = data.writeInt32(length)
?: data.write((uint8_t*)buffer, length);
@@ -95,18 +95,18 @@
}
};
-IMPLEMENT_META_INTERFACE(MediaAnalyticsService, "android.media.IMediaAnalyticsService");
+IMPLEMENT_META_INTERFACE(MediaMetricsService, "android.media.IMediaMetricsService");
// ----------------------------------------------------------------------
-status_t BnMediaAnalyticsService::onTransact(
+status_t BnMediaMetricsService::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch (code) {
case SUBMIT_ITEM: {
- CHECK_INTERFACE(IMediaAnalyticsService, data, reply);
+ CHECK_INTERFACE(IMediaMetricsService, data, reply);
- MediaAnalyticsItem * const item = MediaAnalyticsItem::create();
+ mediametrics::Item * const item = mediametrics::Item::create();
status_t status = item->readFromParcel(data);
if (status != NO_ERROR) { // assume failure logged in item
return status;
@@ -116,7 +116,7 @@
return NO_ERROR;
}
case SUBMIT_BUFFER: {
- CHECK_INTERFACE(IMediaAnalyticsService, data, reply);
+ CHECK_INTERFACE(IMediaMetricsService, data, reply);
int32_t length;
status_t status = data.readInt32(&length);
if (status != NO_ERROR || length <= 0) {
diff --git a/media/libmediametrics/MediaAnalyticsItem.cpp b/media/libmediametrics/MediaAnalyticsItem.cpp
deleted file mode 100644
index 8025e49..0000000
--- a/media/libmediametrics/MediaAnalyticsItem.cpp
+++ /dev/null
@@ -1,1039 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "MediaAnalyticsItem"
-
-#include <inttypes.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-
-#include <mutex>
-#include <set>
-
-#include <binder/Parcel.h>
-#include <utils/Errors.h>
-#include <utils/Log.h>
-#include <utils/SortedVector.h>
-#include <utils/threads.h>
-
-#include <binder/IServiceManager.h>
-#include <media/IMediaAnalyticsService.h>
-#include <media/MediaAnalyticsItem.h>
-#include <private/android_filesystem_config.h>
-
-// Max per-property string size before truncation in toString().
-// Do not make too large, as this is used for dumpsys purposes.
-static constexpr size_t kMaxPropertyStringSize = 4096;
-
-namespace android {
-
-#define DEBUG_SERVICEACCESS 0
-#define DEBUG_API 0
-#define DEBUG_ALLOCATIONS 0
-
-// after this many failed attempts, we stop trying [from this process] and just say that
-// the service is off.
-#define SVC_TRIES 2
-
-MediaAnalyticsItem* MediaAnalyticsItem::convert(mediametrics_handle_t handle) {
- MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
- return item;
-}
-
-mediametrics_handle_t MediaAnalyticsItem::convert(MediaAnalyticsItem *item ) {
- mediametrics_handle_t handle = (mediametrics_handle_t) item;
- return handle;
-}
-
-MediaAnalyticsItem::~MediaAnalyticsItem() {
- if (DEBUG_ALLOCATIONS) {
- ALOGD("Destroy MediaAnalyticsItem @ %p", this);
- }
- clear();
-}
-
-void MediaAnalyticsItem::clear() {
-
- // clean allocated storage from key
- mKey.clear();
-
- // clean attributes
- // contents of the attributes
- for (size_t i = 0 ; i < mPropCount; i++ ) {
- mProps[i].clear();
- }
- // the attribute records themselves
- if (mProps != NULL) {
- free(mProps);
- mProps = NULL;
- }
- mPropSize = 0;
- mPropCount = 0;
-
- return;
-}
-
-// make a deep copy of myself
-MediaAnalyticsItem *MediaAnalyticsItem::dup() {
- MediaAnalyticsItem *dst = new MediaAnalyticsItem(this->mKey);
-
- if (dst != NULL) {
- // key as part of constructor
- dst->mPid = this->mPid;
- dst->mUid = this->mUid;
- dst->mPkgName = this->mPkgName;
- dst->mPkgVersionCode = this->mPkgVersionCode;
- dst->mTimestamp = this->mTimestamp;
-
- // properties aka attributes
- dst->growProps(this->mPropCount);
- for(size_t i=0;i<mPropCount;i++) {
- dst->mProps[i] = this->mProps[i];
- }
- dst->mPropCount = this->mPropCount;
- }
-
- return dst;
-}
-
-MediaAnalyticsItem &MediaAnalyticsItem::setTimestamp(nsecs_t ts) {
- mTimestamp = ts;
- return *this;
-}
-
-nsecs_t MediaAnalyticsItem::getTimestamp() const {
- return mTimestamp;
-}
-
-MediaAnalyticsItem &MediaAnalyticsItem::setPid(pid_t pid) {
- mPid = pid;
- return *this;
-}
-
-pid_t MediaAnalyticsItem::getPid() const {
- return mPid;
-}
-
-MediaAnalyticsItem &MediaAnalyticsItem::setUid(uid_t uid) {
- mUid = uid;
- return *this;
-}
-
-uid_t MediaAnalyticsItem::getUid() const {
- return mUid;
-}
-
-MediaAnalyticsItem &MediaAnalyticsItem::setPkgName(const std::string &pkgName) {
- mPkgName = pkgName;
- return *this;
-}
-
-MediaAnalyticsItem &MediaAnalyticsItem::setPkgVersionCode(int64_t pkgVersionCode) {
- mPkgVersionCode = pkgVersionCode;
- return *this;
-}
-
-int64_t MediaAnalyticsItem::getPkgVersionCode() const {
- return mPkgVersionCode;
-}
-
-
-// find the proper entry in the list
-size_t MediaAnalyticsItem::findPropIndex(const char *name) const
-{
- size_t i = 0;
- for (; i < mPropCount; i++) {
- if (mProps[i].isNamed(name)) break;
- }
- return i;
-}
-
-MediaAnalyticsItem::Prop *MediaAnalyticsItem::findProp(const char *name) const {
- const size_t i = findPropIndex(name);
- if (i < mPropCount) {
- return &mProps[i];
- }
- return nullptr;
-}
-
-// consider this "find-or-allocate".
-// caller validates type and uses clearPropValue() accordingly
-MediaAnalyticsItem::Prop *MediaAnalyticsItem::allocateProp(const char *name) {
- const size_t i = findPropIndex(name);
- if (i < mPropCount) {
- return &mProps[i]; // already have it, return
- }
-
- Prop *prop = allocateProp(); // get a new prop
- if (prop == nullptr) return nullptr;
- prop->setName(name);
- return prop;
-}
-
-MediaAnalyticsItem::Prop *MediaAnalyticsItem::allocateProp() {
- if (mPropCount == mPropSize && growProps() == false) {
- ALOGE("%s: failed allocation for new properties", __func__);
- return nullptr;
- }
- return &mProps[mPropCount++];
-}
-
-// used within the summarizers; return whether property existed
-bool MediaAnalyticsItem::removeProp(const char *name) {
- const size_t i = findPropIndex(name);
- if (i < mPropCount) {
- mProps[i].clear();
- if (i != mPropCount-1) {
- // in the middle, bring last one down to fill gap
- mProps[i].swap(mProps[mPropCount-1]);
- }
- mPropCount--;
- return true;
- }
- return false;
-}
-
-// remove indicated keys and their values
-// return value is # keys removed
-size_t MediaAnalyticsItem::filter(size_t n, const char *attrs[]) {
- size_t zapped = 0;
- for (size_t i = 0; i < n; ++i) {
- const char *name = attrs[i];
- size_t j = findPropIndex(name);
- if (j >= mPropCount) {
- // not there
- continue;
- } else if (j + 1 == mPropCount) {
- // last one, shorten
- zapped++;
- mProps[j].clear();
- mPropCount--;
- } else {
- // in the middle, bring last one down and shorten
- zapped++;
- mProps[j].clear();
- mProps[j] = mProps[mPropCount-1];
- mPropCount--;
- }
- }
- return zapped;
-}
-
-// remove any keys NOT in the provided list
-// return value is # keys removed
-size_t MediaAnalyticsItem::filterNot(size_t n, const char *attrs[]) {
- std::set<std::string> check(attrs, attrs + n);
- size_t zapped = 0;
- for (size_t j = 0; j < mPropCount;) {
- if (check.find(mProps[j].getName()) != check.end()) {
- ++j;
- continue;
- }
- if (j + 1 == mPropCount) {
- // last one, shorten
- zapped++;
- mProps[j].clear();
- mPropCount--;
- break;
- } else {
- // in the middle, bring last one down and shorten
- zapped++;
- mProps[j].clear();
- mProps[j] = mProps[mPropCount-1];
- mPropCount--;
- }
- }
- return zapped;
-}
-
-bool MediaAnalyticsItem::growProps(int increment)
-{
- if (increment <= 0) {
- increment = kGrowProps;
- }
- int nsize = mPropSize + increment;
- Prop *ni = (Prop *)realloc(mProps, sizeof(Prop) * nsize);
-
- if (ni != NULL) {
- for (int i = mPropSize; i < nsize; i++) {
- new (&ni[i]) Prop(); // placement new
- }
- mProps = ni;
- mPropSize = nsize;
- return true;
- } else {
- ALOGW("MediaAnalyticsItem::growProps fails");
- return false;
- }
-}
-
-// Parcel / serialize things for binder calls
-//
-
-status_t MediaAnalyticsItem::readFromParcel(const Parcel& data) {
- int32_t version;
- status_t status = data.readInt32(&version);
- if (status != NO_ERROR) return status;
-
- switch (version) {
- case 0:
- return readFromParcel0(data);
- default:
- ALOGE("%s: unsupported parcel version: %d", __func__, version);
- return INVALID_OPERATION;
- }
-}
-
-status_t MediaAnalyticsItem::readFromParcel0(const Parcel& data) {
- const char *s = data.readCString();
- mKey = s == nullptr ? "" : s;
- int32_t pid, uid;
- status_t status = data.readInt32(&pid) ?: data.readInt32(&uid);
- if (status != NO_ERROR) return status;
- mPid = (pid_t)pid;
- mUid = (uid_t)uid;
- s = data.readCString();
- mPkgName = s == nullptr ? "" : s;
- int32_t count;
- int64_t version, timestamp;
- status = data.readInt64(&version) ?: data.readInt64(×tamp) ?: data.readInt32(&count);
- if (status != NO_ERROR) return status;
- if (count < 0) return BAD_VALUE;
- mPkgVersionCode = version;
- mTimestamp = timestamp;
- for (int i = 0; i < count ; i++) {
- Prop *prop = allocateProp();
- status_t status = prop->readFromParcel(data);
- if (status != NO_ERROR) return status;
- }
- return NO_ERROR;
-}
-
-status_t MediaAnalyticsItem::writeToParcel(Parcel *data) const {
- if (data == nullptr) return BAD_VALUE;
-
- const int32_t version = 0;
- status_t status = data->writeInt32(version);
- if (status != NO_ERROR) return status;
-
- switch (version) {
- case 0:
- return writeToParcel0(data);
- default:
- ALOGE("%s: unsupported parcel version: %d", __func__, version);
- return INVALID_OPERATION;
- }
-}
-
-status_t MediaAnalyticsItem::writeToParcel0(Parcel *data) const {
- status_t status =
- data->writeCString(mKey.c_str())
- ?: data->writeInt32(mPid)
- ?: data->writeInt32(mUid)
- ?: data->writeCString(mPkgName.c_str())
- ?: data->writeInt64(mPkgVersionCode)
- ?: data->writeInt64(mTimestamp);
- if (status != NO_ERROR) return status;
-
- data->writeInt32((int32_t)mPropCount);
- for (size_t i = 0 ; i < mPropCount; ++i) {
- status = mProps[i].writeToParcel(data);
- if (status != NO_ERROR) return status;
- }
- return NO_ERROR;
-}
-
-const char *MediaAnalyticsItem::toCString() {
- return toCString(PROTO_LAST);
-}
-
-const char * MediaAnalyticsItem::toCString(int version) {
- std::string val = toString(version);
- return strdup(val.c_str());
-}
-
-std::string MediaAnalyticsItem::toString() const {
- return toString(PROTO_LAST);
-}
-
-std::string MediaAnalyticsItem::toString(int version) const {
- std::string result;
- char buffer[kMaxPropertyStringSize];
-
- snprintf(buffer, sizeof(buffer), "[%d:%s:%d:%d:%lld:%s:%zu:",
- version, mKey.c_str(), mPid, mUid, (long long)mTimestamp,
- mPkgName.c_str(), mPropCount);
- result.append(buffer);
- for (size_t i = 0 ; i < mPropCount; ++i) {
- mProps[i].toString(buffer, sizeof(buffer));
- result.append(buffer);
- }
- result.append("]");
- return result;
-}
-
-// for the lazy, we offer methods that finds the service and
-// calls the appropriate daemon
-bool MediaAnalyticsItem::selfrecord() {
- ALOGD_IF(DEBUG_API, "%s: delivering %s", __func__, this->toString().c_str());
- sp<IMediaAnalyticsService> svc = getInstance();
- if (svc != NULL) {
- status_t status = svc->submit(this);
- if (status != NO_ERROR) {
- ALOGW("%s: failed to record: %s", __func__, this->toString().c_str());
- return false;
- }
- return true;
- } else {
- return false;
- }
-}
-
-namespace mediametrics {
-//static
-bool BaseItem::isEnabled() {
- // completely skip logging from certain UIDs. We do this here
- // to avoid the multi-second timeouts while we learn that
- // sepolicy will not let us find the service.
- // We do this only for a select set of UIDs
- // The sepolicy protection is still in place, we just want a faster
- // response from this specific, small set of uids.
-
- // This is checked only once in the lifetime of the process.
- const uid_t uid = getuid();
- switch (uid) {
- case AID_RADIO: // telephony subsystem, RIL
- return false;
- }
-
- int enabled = property_get_int32(MediaAnalyticsItem::EnabledProperty, -1);
- if (enabled == -1) {
- enabled = property_get_int32(MediaAnalyticsItem::EnabledPropertyPersist, -1);
- }
- if (enabled == -1) {
- enabled = MediaAnalyticsItem::EnabledProperty_default;
- }
- return enabled > 0;
-}
-
-// monitor health of our connection to the metrics service
-class MediaMetricsDeathNotifier : public IBinder::DeathRecipient {
- virtual void binderDied(const wp<IBinder> &) {
- ALOGW("Reacquire service connection on next request");
- BaseItem::dropInstance();
- }
-};
-
-static sp<MediaMetricsDeathNotifier> sNotifier;
-// static
-sp<IMediaAnalyticsService> BaseItem::sAnalyticsService;
-static std::mutex sServiceMutex;
-static int sRemainingBindAttempts = SVC_TRIES;
-
-// static
-void BaseItem::dropInstance() {
- std::lock_guard _l(sServiceMutex);
- sRemainingBindAttempts = SVC_TRIES;
- sAnalyticsService = nullptr;
-}
-
-// static
-bool BaseItem::submitBuffer(const char *buffer, size_t size) {
-/*
- MediaAnalyticsItem item;
- status_t status = item.readFromByteString(buffer, size);
- ALOGD("%s: status:%d, size:%zu, item:%s", __func__, status, size, item.toString().c_str());
- return item.selfrecord();
- */
-
- ALOGD_IF(DEBUG_API, "%s: delivering %zu bytes", __func__, size);
- sp<IMediaAnalyticsService> svc = getInstance();
- if (svc != nullptr) {
- const status_t status = svc->submitBuffer(buffer, size);
- if (status != NO_ERROR) {
- ALOGW("%s: failed(%d) to record: %zu bytes", __func__, status, size);
- return false;
- }
- return true;
- }
- return false;
-}
-
-//static
-sp<IMediaAnalyticsService> BaseItem::getInstance() {
- static const char *servicename = "media.metrics";
- static const bool enabled = isEnabled(); // singleton initialized
-
- if (enabled == false) {
- ALOGD_IF(DEBUG_SERVICEACCESS, "disabled");
- return nullptr;
- }
- std::lock_guard _l(sServiceMutex);
- // think of remainingBindAttempts as telling us whether service == nullptr because
- // (1) we haven't tried to initialize it yet
- // (2) we've tried to initialize it, but failed.
- if (sAnalyticsService == nullptr && sRemainingBindAttempts > 0) {
- const char *badness = "";
- sp<IServiceManager> sm = defaultServiceManager();
- if (sm != nullptr) {
- sp<IBinder> binder = sm->getService(String16(servicename));
- if (binder != nullptr) {
- sAnalyticsService = interface_cast<IMediaAnalyticsService>(binder);
- sNotifier = new MediaMetricsDeathNotifier();
- binder->linkToDeath(sNotifier);
- } else {
- badness = "did not find service";
- }
- } else {
- badness = "No Service Manager access";
- }
- if (sAnalyticsService == nullptr) {
- if (sRemainingBindAttempts > 0) {
- sRemainingBindAttempts--;
- }
- ALOGD_IF(DEBUG_SERVICEACCESS, "%s: unable to bind to service %s: %s",
- __func__, servicename, badness);
- }
- }
- return sAnalyticsService;
-}
-
-} // namespace mediametrics
-
-// merge the info from 'incoming' into this record.
-// we finish with a union of this+incoming and special handling for collisions
-bool MediaAnalyticsItem::merge(MediaAnalyticsItem *incoming) {
-
- // if I don't have key or session id, take them from incoming
- // 'this' should never be missing both of them...
- if (mKey.empty()) {
- mKey = incoming->mKey;
- }
-
- // for each attribute from 'incoming', resolve appropriately
- int nattr = incoming->mPropCount;
- for (int i = 0 ; i < nattr; i++ ) {
- Prop *iprop = &incoming->mProps[i];
- const char *p = iprop->mName;
- size_t len = strlen(p);
-
- // should ignore a zero length name...
- if (len == 0) {
- continue;
- }
-
- Prop *oprop = findProp(iprop->mName);
-
- if (oprop == NULL) {
- // no oprop, so we insert the new one
- oprop = allocateProp(p);
- if (oprop != NULL) {
- *oprop = *iprop;
- } else {
- ALOGW("dropped property '%s'", iprop->mName);
- }
- } else {
- *oprop = *iprop;
- }
- }
-
- // not sure when we'd return false...
- return true;
-}
-
-namespace {
-
-template <typename T>
-status_t insert(const T& val, char **bufferpptr, char *bufferptrmax)
-{
- const size_t size = sizeof(val);
- if (*bufferpptr + size > bufferptrmax) {
- ALOGE("%s: buffer exceeded with size %zu", __func__, size);
- return BAD_VALUE;
- }
- memcpy(*bufferpptr, &val, size);
- *bufferpptr += size;
- return NO_ERROR;
-}
-
-template <>
-status_t insert(const char * const& val, char **bufferpptr, char *bufferptrmax)
-{
- const size_t size = strlen(val) + 1;
- if (size > UINT16_MAX || *bufferpptr + size > bufferptrmax) {
- ALOGE("%s: buffer exceeded with size %zu", __func__, size);
- return BAD_VALUE;
- }
- memcpy(*bufferpptr, val, size);
- *bufferpptr += size;
- return NO_ERROR;
-}
-
-template <>
- __unused
-status_t insert(char * const& val, char **bufferpptr, char *bufferptrmax)
-{
- return insert((const char *)val, bufferpptr, bufferptrmax);
-}
-
-template <typename T>
-status_t extract(T *val, const char **bufferpptr, const char *bufferptrmax)
-{
- const size_t size = sizeof(*val);
- if (*bufferpptr + size > bufferptrmax) {
- ALOGE("%s: buffer exceeded with size %zu", __func__, size);
- return BAD_VALUE;
- }
- memcpy(val, *bufferpptr, size);
- *bufferpptr += size;
- return NO_ERROR;
-}
-
-template <>
-status_t extract(char **val, const char **bufferpptr, const char *bufferptrmax)
-{
- const char *ptr = *bufferpptr;
- while (*ptr != 0) {
- if (ptr >= bufferptrmax) {
- ALOGE("%s: buffer exceeded", __func__);
- return BAD_VALUE;
- }
- ++ptr;
- }
- const size_t size = (ptr - *bufferpptr) + 1;
- *val = (char *)malloc(size);
- memcpy(*val, *bufferpptr, size);
- *bufferpptr += size;
- return NO_ERROR;
-}
-
-} // namespace
-
-status_t MediaAnalyticsItem::writeToByteString(char **pbuffer, size_t *plength) const
-{
- if (pbuffer == nullptr || plength == nullptr)
- return BAD_VALUE;
-
- // get size
- const size_t keySizeZeroTerminated = strlen(mKey.c_str()) + 1;
- if (keySizeZeroTerminated > UINT16_MAX) {
- ALOGW("%s: key size %zu too large", __func__, keySizeZeroTerminated);
- return INVALID_OPERATION;
- }
- const uint16_t version = 0;
- const uint32_t header_size =
- sizeof(uint32_t) // total size
- + sizeof(header_size) // header size
- + sizeof(version) // encoding version
- + sizeof(uint16_t) // key size
- + keySizeZeroTerminated // key, zero terminated
- + sizeof(int32_t) // pid
- + sizeof(int32_t) // uid
- + sizeof(int64_t) // timestamp
- ;
-
- uint32_t size = header_size
- + sizeof(uint32_t) // # properties
- ;
- for (size_t i = 0 ; i < mPropCount; ++i) {
- const size_t propSize = mProps[i].getByteStringSize();
- if (propSize > UINT16_MAX) {
- ALOGW("%s: prop %zu size %zu too large", __func__, i, propSize);
- return INVALID_OPERATION;
- }
- if (__builtin_add_overflow(size, propSize, &size)) {
- ALOGW("%s: item size overflow at property %zu", __func__, i);
- return INVALID_OPERATION;
- }
- }
-
- // since we fill every byte in the buffer (there is no padding),
- // malloc is used here instead of calloc.
- char * const build = (char *)malloc(size);
- if (build == nullptr) return NO_MEMORY;
-
- char *filling = build;
- char *buildmax = build + size;
- if (insert((uint32_t)size, &filling, buildmax) != NO_ERROR
- || insert(header_size, &filling, buildmax) != NO_ERROR
- || insert(version, &filling, buildmax) != NO_ERROR
- || insert((uint16_t)keySizeZeroTerminated, &filling, buildmax) != NO_ERROR
- || insert(mKey.c_str(), &filling, buildmax) != NO_ERROR
- || insert((int32_t)mPid, &filling, buildmax) != NO_ERROR
- || insert((int32_t)mUid, &filling, buildmax) != NO_ERROR
- || insert((int64_t)mTimestamp, &filling, buildmax) != NO_ERROR
- || insert((uint32_t)mPropCount, &filling, buildmax) != NO_ERROR) {
- ALOGE("%s:could not write header", __func__); // shouldn't happen
- free(build);
- return INVALID_OPERATION;
- }
- for (size_t i = 0 ; i < mPropCount; ++i) {
- if (mProps[i].writeToByteString(&filling, buildmax) != NO_ERROR) {
- free(build);
- // shouldn't happen
- ALOGE("%s:could not write prop %zu of %zu", __func__, i, mPropCount);
- return INVALID_OPERATION;
- }
- }
-
- if (filling != buildmax) {
- ALOGE("%s: problems populating; wrote=%d planned=%d",
- __func__, (int)(filling - build), (int)size);
- free(build);
- return INVALID_OPERATION;
- }
- *pbuffer = build;
- *plength = size;
- return NO_ERROR;
-}
-
-status_t MediaAnalyticsItem::readFromByteString(const char *bufferptr, size_t length)
-{
- if (bufferptr == nullptr) return BAD_VALUE;
-
- const char *read = bufferptr;
- const char *readend = bufferptr + length;
-
- uint32_t size;
- uint32_t header_size;
- uint16_t version;
- uint16_t key_size;
- char *key = nullptr;
- int32_t pid;
- int32_t uid;
- int64_t timestamp;
- uint32_t propCount;
- if (extract(&size, &read, readend) != NO_ERROR
- || extract(&header_size, &read, readend) != NO_ERROR
- || extract(&version, &read, readend) != NO_ERROR
- || extract(&key_size, &read, readend) != NO_ERROR
- || extract(&key, &read, readend) != NO_ERROR
- || extract(&pid, &read, readend) != NO_ERROR
- || extract(&uid, &read, readend) != NO_ERROR
- || extract(×tamp, &read, readend) != NO_ERROR
- || size > length
- || strlen(key) + 1 != key_size
- || header_size > size) {
- free(key);
- ALOGW("%s: invalid header", __func__);
- return INVALID_OPERATION;
- }
- mKey = key;
- free(key);
- const size_t pos = read - bufferptr;
- if (pos > header_size) {
- ALOGW("%s: invalid header pos:%zu > header_size:%u",
- __func__, pos, header_size);
- return INVALID_OPERATION;
- } else if (pos < header_size) {
- ALOGW("%s: mismatched header pos:%zu < header_size:%u, advancing",
- __func__, pos, header_size);
- read += (header_size - pos);
- }
- if (extract(&propCount, &read, readend) != NO_ERROR) {
- ALOGD("%s: cannot read prop count", __func__);
- return INVALID_OPERATION;
- }
- mPid = pid;
- mUid = uid;
- mTimestamp = timestamp;
- for (size_t i = 0; i < propCount; ++i) {
- Prop *prop = allocateProp();
- if (prop->readFromByteString(&read, readend) != NO_ERROR) {
- ALOGW("%s: cannot read prop %zu", __func__, i);
- return INVALID_OPERATION;
- }
- }
- return NO_ERROR;
-}
-
-status_t MediaAnalyticsItem::Prop::writeToParcel(Parcel *data) const
-{
- switch (mType) {
- case kTypeInt32:
- return data->writeCString(mName)
- ?: data->writeInt32(mType)
- ?: data->writeInt32(u.int32Value);
- case kTypeInt64:
- return data->writeCString(mName)
- ?: data->writeInt32(mType)
- ?: data->writeInt64(u.int64Value);
- case kTypeDouble:
- return data->writeCString(mName)
- ?: data->writeInt32(mType)
- ?: data->writeDouble(u.doubleValue);
- case kTypeRate:
- return data->writeCString(mName)
- ?: data->writeInt32(mType)
- ?: data->writeInt64(u.rate.first)
- ?: data->writeInt64(u.rate.second);
- case kTypeCString:
- return data->writeCString(mName)
- ?: data->writeInt32(mType)
- ?: data->writeCString(u.CStringValue);
- default:
- ALOGE("%s: found bad type: %d, name %s", __func__, mType, mName);
- return BAD_VALUE;
- }
-}
-
-status_t MediaAnalyticsItem::Prop::readFromParcel(const Parcel& data)
-{
- const char *key = data.readCString();
- if (key == nullptr) return BAD_VALUE;
- int32_t type;
- status_t status = data.readInt32(&type);
- if (status != NO_ERROR) return status;
- switch (type) {
- case kTypeInt32:
- status = data.readInt32(&u.int32Value);
- break;
- case kTypeInt64:
- status = data.readInt64(&u.int64Value);
- break;
- case kTypeDouble:
- status = data.readDouble(&u.doubleValue);
- break;
- case kTypeCString: {
- const char *s = data.readCString();
- if (s == nullptr) return BAD_VALUE;
- set(s);
- break;
- }
- case kTypeRate: {
- std::pair<int64_t, int64_t> rate;
- status = data.readInt64(&rate.first)
- ?: data.readInt64(&rate.second);
- if (status == NO_ERROR) {
- set(rate);
- }
- break;
- }
- default:
- ALOGE("%s: reading bad item type: %d", __func__, mType);
- return BAD_VALUE;
- }
- if (status == NO_ERROR) {
- setName(key);
- mType = (Type)type;
- }
- return status;
-}
-
-void MediaAnalyticsItem::Prop::toString(char *buffer, size_t length) const
-{
- switch (mType) {
- case kTypeInt32:
- snprintf(buffer, length, "%s=%d:", mName, u.int32Value);
- break;
- case MediaAnalyticsItem::kTypeInt64:
- snprintf(buffer, length, "%s=%lld:", mName, (long long)u.int64Value);
- break;
- case MediaAnalyticsItem::kTypeDouble:
- snprintf(buffer, length, "%s=%e:", mName, u.doubleValue);
- break;
- case MediaAnalyticsItem::kTypeRate:
- snprintf(buffer, length, "%s=%lld/%lld:",
- mName, (long long)u.rate.first, (long long)u.rate.second);
- break;
- case MediaAnalyticsItem::kTypeCString:
- // TODO sanitize string for ':' '='
- snprintf(buffer, length, "%s=%s:", mName, u.CStringValue);
- break;
- default:
- ALOGE("%s: bad item type: %d for %s", __func__, mType, mName);
- if (length > 0) buffer[0] = 0;
- break;
- }
-}
-
-size_t MediaAnalyticsItem::Prop::getByteStringSize() const
-{
- const size_t header =
- sizeof(uint16_t) // length
- + sizeof(uint8_t) // type
- + strlen(mName) + 1; // mName + 0 termination
- size_t payload = 0;
- switch (mType) {
- case MediaAnalyticsItem::kTypeInt32:
- payload = sizeof(u.int32Value);
- break;
- case MediaAnalyticsItem::kTypeInt64:
- payload = sizeof(u.int64Value);
- break;
- case MediaAnalyticsItem::kTypeDouble:
- payload = sizeof(u.doubleValue);
- break;
- case MediaAnalyticsItem::kTypeRate:
- payload = sizeof(u.rate.first) + sizeof(u.rate.second);
- break;
- case MediaAnalyticsItem::kTypeCString:
- payload = strlen(u.CStringValue) + 1;
- break;
- default:
- ALOGE("%s: found bad prop type: %d, name %s",
- __func__, mType, mName); // no payload computed
- break;
- }
- return header + payload;
-}
-
-namespace mediametrics {
-
-// TODO: fold into a template later.
-status_t BaseItem::writeToByteString(
- const char *name, int32_t value, char **bufferpptr, char *bufferptrmax)
-{
- const size_t len = 2 + 1 + strlen(name) + 1 + sizeof(value);
- if (len > UINT16_MAX) return BAD_VALUE;
- return insert((uint16_t)len, bufferpptr, bufferptrmax)
- ?: insert((uint8_t)kTypeInt32, bufferpptr, bufferptrmax)
- ?: insert(name, bufferpptr, bufferptrmax)
- ?: insert(value, bufferpptr, bufferptrmax);
-}
-
-status_t BaseItem::writeToByteString(
- const char *name, int64_t value, char **bufferpptr, char *bufferptrmax)
-{
- const size_t len = 2 + 1 + strlen(name) + 1 + sizeof(value);
- if (len > UINT16_MAX) return BAD_VALUE;
- return insert((uint16_t)len, bufferpptr, bufferptrmax)
- ?: insert((uint8_t)kTypeInt64, bufferpptr, bufferptrmax)
- ?: insert(name, bufferpptr, bufferptrmax)
- ?: insert(value, bufferpptr, bufferptrmax);
-}
-
-status_t BaseItem::writeToByteString(
- const char *name, double value, char **bufferpptr, char *bufferptrmax)
-{
- const size_t len = 2 + 1 + strlen(name) + 1 + sizeof(value);
- if (len > UINT16_MAX) return BAD_VALUE;
- return insert((uint16_t)len, bufferpptr, bufferptrmax)
- ?: insert((uint8_t)kTypeDouble, bufferpptr, bufferptrmax)
- ?: insert(name, bufferpptr, bufferptrmax)
- ?: insert(value, bufferpptr, bufferptrmax);
-}
-
-status_t BaseItem::writeToByteString(
- const char *name, const std::pair<int64_t, int64_t> &value, char **bufferpptr, char *bufferptrmax)
-{
- const size_t len = 2 + 1 + strlen(name) + 1 + 8 + 8;
- if (len > UINT16_MAX) return BAD_VALUE;
- return insert((uint16_t)len, bufferpptr, bufferptrmax)
- ?: insert((uint8_t)kTypeRate, bufferpptr, bufferptrmax)
- ?: insert(name, bufferpptr, bufferptrmax)
- ?: insert(value.first, bufferpptr, bufferptrmax)
- ?: insert(value.second, bufferpptr, bufferptrmax);
-}
-
-status_t BaseItem::writeToByteString(
- const char *name, char * const &value, char **bufferpptr, char *bufferptrmax)
-{
- return writeToByteString(name, (const char *)value, bufferpptr, bufferptrmax);
-}
-
-status_t BaseItem::writeToByteString(
- const char *name, const char * const &value, char **bufferpptr, char *bufferptrmax)
-{
- const size_t len = 2 + 1 + strlen(name) + 1 + strlen(value) + 1;
- if (len > UINT16_MAX) return BAD_VALUE;
- return insert((uint16_t)len, bufferpptr, bufferptrmax)
- ?: insert((uint8_t)kTypeCString, bufferpptr, bufferptrmax)
- ?: insert(name, bufferpptr, bufferptrmax)
- ?: insert(value, bufferpptr, bufferptrmax);
-}
-
-
-status_t BaseItem::writeToByteString(
- const char *name, const none_t &, char **bufferpptr, char *bufferptrmax)
-{
- const size_t len = 2 + 1 + strlen(name) + 1;
- if (len > UINT16_MAX) return BAD_VALUE;
- return insert((uint16_t)len, bufferpptr, bufferptrmax)
- ?: insert((uint8_t)kTypeCString, bufferpptr, bufferptrmax)
- ?: insert(name, bufferpptr, bufferptrmax);
-}
-
-} // namespace mediametrics
-
-status_t MediaAnalyticsItem::Prop::writeToByteString(
- char **bufferpptr, char *bufferptrmax) const
-{
- switch (mType) {
- case kTypeInt32:
- return BaseItem::writeToByteString(mName, u.int32Value, bufferpptr, bufferptrmax);
- case kTypeInt64:
- return BaseItem::writeToByteString(mName, u.int64Value, bufferpptr, bufferptrmax);
- case kTypeDouble:
- return BaseItem::writeToByteString(mName, u.doubleValue, bufferpptr, bufferptrmax);
- case kTypeRate:
- return BaseItem::writeToByteString(mName, u.rate, bufferpptr, bufferptrmax);
- case kTypeCString:
- return BaseItem::writeToByteString(mName, u.CStringValue, bufferpptr, bufferptrmax);
- case kTypeNone:
- return BaseItem::writeToByteString(mName, none_t{}, bufferpptr, bufferptrmax);
- default:
- ALOGE("%s: found bad prop type: %d, name %s",
- __func__, mType, mName); // no payload sent
- return BAD_VALUE;
- }
-}
-
-status_t MediaAnalyticsItem::Prop::readFromByteString(
- const char **bufferpptr, const char *bufferptrmax)
-{
- uint16_t len;
- char *name;
- uint8_t type;
- status_t status = extract(&len, bufferpptr, bufferptrmax)
- ?: extract(&type, bufferpptr, bufferptrmax)
- ?: extract(&name, bufferpptr, bufferptrmax);
- if (status != NO_ERROR) return status;
- if (mName != nullptr) {
- free(mName);
- }
- mName = name;
- if (mType == kTypeCString) {
- free(u.CStringValue);
- u.CStringValue = nullptr;
- }
- mType = (Type)type;
- switch (mType) {
- case kTypeInt32:
- return extract(&u.int32Value, bufferpptr, bufferptrmax);
- case kTypeInt64:
- return extract(&u.int64Value, bufferpptr, bufferptrmax);
- case kTypeDouble:
- return extract(&u.doubleValue, bufferpptr, bufferptrmax);
- case kTypeRate:
- return extract(&u.rate.first, bufferpptr, bufferptrmax)
- ?: extract(&u.rate.second, bufferpptr, bufferptrmax);
- case kTypeCString:
- status = extract(&u.CStringValue, bufferpptr, bufferptrmax);
- if (status != NO_ERROR) mType = kTypeNone;
- return status;
- case kTypeNone:
- return NO_ERROR;
- default:
- mType = kTypeNone;
- ALOGE("%s: found bad prop type: %d, name %s",
- __func__, mType, mName); // no payload sent
- return BAD_VALUE;
- }
-}
-
-} // namespace android
diff --git a/media/libmediametrics/MediaMetrics.cpp b/media/libmediametrics/MediaMetrics.cpp
index cf268e0..a3c2f1a 100644
--- a/media/libmediametrics/MediaMetrics.cpp
+++ b/media/libmediametrics/MediaMetrics.cpp
@@ -21,7 +21,7 @@
#include <string.h>
#include <sys/types.h>
-#include <media/MediaAnalyticsItem.h>
+#include <media/MediaMetricsItem.h>
#include <media/MediaMetrics.h>
//
@@ -31,28 +31,31 @@
// ALL functions returning a char * give responsibility for the allocated buffer
// to the caller. The caller is responsible to call free() on that pointer.
//
+//
+
+using namespace android::mediametrics;
// manage the overall record
mediametrics_handle_t mediametrics_create(mediametricskey_t key) {
- android::MediaAnalyticsItem *item = android::MediaAnalyticsItem::create(key);
+ Item *item = Item::create(key);
return (mediametrics_handle_t) item;
}
void mediametrics_delete(mediametrics_handle_t handle) {
- android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
+ Item *item = (Item *) handle;
if (item == NULL) return;
delete item;
}
mediametricskey_t mediametrics_getKey(mediametrics_handle_t handle) {
- android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
+ Item *item = (Item *) handle;
if (item == NULL) return NULL;
return strdup(item->getKey().c_str());
}
// nuplayer, et al use it when acting as proxies
void mediametrics_setUid(mediametrics_handle_t handle, uid_t uid) {
- android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
+ Item *item = (Item *) handle;
if (item != NULL) item->setUid(uid);
}
@@ -61,31 +64,31 @@
void mediametrics_setInt32(mediametrics_handle_t handle, attr_t attr,
int32_t value) {
- android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
+ Item *item = (Item *) handle;
if (item != NULL) item->setInt32(attr, value);
}
void mediametrics_setInt64(mediametrics_handle_t handle, attr_t attr,
int64_t value) {
- android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
+ Item *item = (Item *) handle;
if (item != NULL) item->setInt64(attr, value);
}
void mediametrics_setDouble(mediametrics_handle_t handle, attr_t attr,
double value) {
- android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
+ Item *item = (Item *) handle;
if (item != NULL) item->setDouble(attr, value);
}
void mediametrics_setRate(mediametrics_handle_t handle, attr_t attr,
int64_t count, int64_t duration) {
- android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
+ Item *item = (Item *) handle;
if (item != NULL) item->setRate(attr, count, duration);
}
void mediametrics_setCString(mediametrics_handle_t handle, attr_t attr,
const char *value) {
- android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
+ Item *item = (Item *) handle;
if (item != NULL) item->setCString(attr, value);
}
@@ -94,25 +97,25 @@
void mediametrics_addInt32(mediametrics_handle_t handle, attr_t attr,
int32_t value) {
- android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
+ Item *item = (Item *) handle;
if (item != NULL) item->addInt32(attr, value);
}
void mediametrics_addInt64(mediametrics_handle_t handle, attr_t attr,
int64_t value) {
- android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
+ Item *item = (Item *) handle;
if (item != NULL) item->addInt64(attr, value);
}
void mediametrics_addDouble(mediametrics_handle_t handle, attr_t attr,
double value) {
- android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
+ Item *item = (Item *) handle;
if (item != NULL) item->addDouble(attr, value);
}
void mediametrics_addRate(mediametrics_handle_t handle, attr_t attr,
int64_t count, int64_t duration) {
- android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
+ Item *item = (Item *) handle;
if (item != NULL) item->addRate(attr, count, duration);
}
@@ -123,28 +126,28 @@
bool mediametrics_getInt32(mediametrics_handle_t handle, attr_t attr,
int32_t * value) {
- android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
+ Item *item = (Item *) handle;
if (item == NULL) return false;
return item->getInt32(attr, value);
}
bool mediametrics_getInt64(mediametrics_handle_t handle, attr_t attr,
int64_t * value) {
- android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
+ Item *item = (Item *) handle;
if (item == NULL) return false;
return item->getInt64(attr, value);
}
bool mediametrics_getDouble(mediametrics_handle_t handle, attr_t attr,
double *value) {
- android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
+ Item *item = (Item *) handle;
if (item == NULL) return false;
return item->getDouble(attr, value);
}
bool mediametrics_getRate(mediametrics_handle_t handle, attr_t attr,
int64_t * count, int64_t * duration, double *rate) {
- android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
+ Item *item = (Item *) handle;
if (item == NULL) return false;
return item->getRate(attr, count, duration, rate);
}
@@ -152,7 +155,7 @@
// NB: caller owns the string that comes back, is responsible for freeing it
bool mediametrics_getCString(mediametrics_handle_t handle, attr_t attr,
char **value) {
- android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
+ Item *item = (Item *) handle;
if (item == NULL) return false;
return item->getCString(attr, value);
@@ -164,36 +167,36 @@
}
bool mediametrics_selfRecord(mediametrics_handle_t handle) {
- android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
+ Item *item = (Item *) handle;
if (item == NULL) return false;
return item->selfrecord();
}
mediametrics_handle_t mediametrics_dup(mediametrics_handle_t handle) {
- android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
- if (item == NULL) return android::MediaAnalyticsItem::convert(item);
- return android::MediaAnalyticsItem::convert(item->dup());
+ Item *item = (Item *) handle;
+ if (item == NULL) return Item::convert(item);
+ return Item::convert(item->dup());
}
const char *mediametrics_readable(mediametrics_handle_t handle) {
- android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
+ Item *item = (Item *) handle;
if (item == NULL) return "";
return item->toCString();
}
int32_t mediametrics_count(mediametrics_handle_t handle) {
- android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
+ Item *item = (Item *) handle;
if (item == NULL) return 0;
return item->count();
}
bool mediametrics_isEnabled() {
// static, so doesn't need an instance
- return android::MediaAnalyticsItem::isEnabled();
+ return Item::isEnabled();
}
bool mediametrics_getAttributes(mediametrics_handle_t handle, char **buffer, size_t *length) {
- android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
+ Item *item = (Item *) handle;
if (item == NULL) return false;
return item->writeToByteString(buffer, length) == android::NO_ERROR;
diff --git a/media/libmediametrics/MediaMetricsItem.cpp b/media/libmediametrics/MediaMetricsItem.cpp
new file mode 100644
index 0000000..4371668
--- /dev/null
+++ b/media/libmediametrics/MediaMetricsItem.cpp
@@ -0,0 +1,642 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "mediametrics::Item"
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <mutex>
+#include <set>
+
+#include <binder/Parcel.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <utils/SortedVector.h>
+#include <utils/threads.h>
+
+#include <binder/IServiceManager.h>
+#include <media/IMediaMetricsService.h>
+#include <media/MediaMetricsItem.h>
+#include <private/android_filesystem_config.h>
+
+// Max per-property string size before truncation in toString().
+// Do not make too large, as this is used for dumpsys purposes.
+static constexpr size_t kMaxPropertyStringSize = 4096;
+
+namespace android::mediametrics {
+
+#define DEBUG_SERVICEACCESS 0
+#define DEBUG_API 0
+#define DEBUG_ALLOCATIONS 0
+
+// after this many failed attempts, we stop trying [from this process] and just say that
+// the service is off.
+#define SVC_TRIES 2
+
+mediametrics::Item* mediametrics::Item::convert(mediametrics_handle_t handle) {
+ mediametrics::Item *item = (android::mediametrics::Item *) handle;
+ return item;
+}
+
+mediametrics_handle_t mediametrics::Item::convert(mediametrics::Item *item ) {
+ mediametrics_handle_t handle = (mediametrics_handle_t) item;
+ return handle;
+}
+
+mediametrics::Item::~Item() {
+ if (DEBUG_ALLOCATIONS) {
+ ALOGD("Destroy mediametrics::Item @ %p", this);
+ }
+}
+
+mediametrics::Item &mediametrics::Item::setTimestamp(nsecs_t ts) {
+ mTimestamp = ts;
+ return *this;
+}
+
+nsecs_t mediametrics::Item::getTimestamp() const {
+ return mTimestamp;
+}
+
+mediametrics::Item &mediametrics::Item::setPid(pid_t pid) {
+ mPid = pid;
+ return *this;
+}
+
+pid_t mediametrics::Item::getPid() const {
+ return mPid;
+}
+
+mediametrics::Item &mediametrics::Item::setUid(uid_t uid) {
+ mUid = uid;
+ return *this;
+}
+
+uid_t mediametrics::Item::getUid() const {
+ return mUid;
+}
+
+mediametrics::Item &mediametrics::Item::setPkgName(const std::string &pkgName) {
+ mPkgName = pkgName;
+ return *this;
+}
+
+mediametrics::Item &mediametrics::Item::setPkgVersionCode(int64_t pkgVersionCode) {
+ mPkgVersionCode = pkgVersionCode;
+ return *this;
+}
+
+int64_t mediametrics::Item::getPkgVersionCode() const {
+ return mPkgVersionCode;
+}
+
+// remove indicated keys and their values
+// return value is # keys removed
+size_t mediametrics::Item::filter(size_t n, const char *attrs[]) {
+ size_t zapped = 0;
+ for (size_t i = 0; i < n; ++i) {
+ zapped += mProps.erase(attrs[i]);
+ }
+ return zapped;
+}
+
+// remove any keys NOT in the provided list
+// return value is # keys removed
+size_t mediametrics::Item::filterNot(size_t n, const char *attrs[]) {
+ std::set<std::string> check(attrs, attrs + n);
+ size_t zapped = 0;
+ for (auto it = mProps.begin(); it != mProps.end();) {
+ if (check.find(it->first) != check.end()) {
+ ++it;
+ } else {
+ it = mProps.erase(it);
+ ++zapped;
+ }
+ }
+ return zapped;
+}
+
+// Parcel / serialize things for binder calls
+//
+
+status_t mediametrics::Item::readFromParcel(const Parcel& data) {
+ int32_t version;
+ status_t status = data.readInt32(&version);
+ if (status != NO_ERROR) return status;
+
+ switch (version) {
+ case 0:
+ return readFromParcel0(data);
+ default:
+ ALOGE("%s: unsupported parcel version: %d", __func__, version);
+ return INVALID_OPERATION;
+ }
+}
+
+status_t mediametrics::Item::readFromParcel0(const Parcel& data) {
+ const char *s = data.readCString();
+ mKey = s == nullptr ? "" : s;
+ int32_t pid, uid;
+ status_t status = data.readInt32(&pid) ?: data.readInt32(&uid);
+ if (status != NO_ERROR) return status;
+ mPid = (pid_t)pid;
+ mUid = (uid_t)uid;
+ s = data.readCString();
+ mPkgName = s == nullptr ? "" : s;
+ int32_t count;
+ int64_t version, timestamp;
+ status = data.readInt64(&version) ?: data.readInt64(×tamp) ?: data.readInt32(&count);
+ if (status != NO_ERROR) return status;
+ if (count < 0) return BAD_VALUE;
+ mPkgVersionCode = version;
+ mTimestamp = timestamp;
+ for (int i = 0; i < count; i++) {
+ Prop prop;
+ status_t status = prop.readFromParcel(data);
+ if (status != NO_ERROR) return status;
+ mProps[prop.getName()] = std::move(prop);
+ }
+ return NO_ERROR;
+}
+
+status_t mediametrics::Item::writeToParcel(Parcel *data) const {
+ if (data == nullptr) return BAD_VALUE;
+
+ const int32_t version = 0;
+ status_t status = data->writeInt32(version);
+ if (status != NO_ERROR) return status;
+
+ switch (version) {
+ case 0:
+ return writeToParcel0(data);
+ default:
+ ALOGE("%s: unsupported parcel version: %d", __func__, version);
+ return INVALID_OPERATION;
+ }
+}
+
+status_t mediametrics::Item::writeToParcel0(Parcel *data) const {
+ status_t status =
+ data->writeCString(mKey.c_str())
+ ?: data->writeInt32(mPid)
+ ?: data->writeInt32(mUid)
+ ?: data->writeCString(mPkgName.c_str())
+ ?: data->writeInt64(mPkgVersionCode)
+ ?: data->writeInt64(mTimestamp);
+ if (status != NO_ERROR) return status;
+
+ data->writeInt32((int32_t)mProps.size());
+ for (auto &prop : *this) {
+ status = prop.writeToParcel(data);
+ if (status != NO_ERROR) return status;
+ }
+ return NO_ERROR;
+}
+
+const char *mediametrics::Item::toCString() {
+ std::string val = toString();
+ return strdup(val.c_str());
+}
+
+/*
+ * Similar to audio_utils/clock.h but customized for displaying mediametrics time.
+ */
+
+void nsToString(int64_t ns, char *buffer, size_t bufferSize, PrintFormat format)
+{
+ if (bufferSize == 0) return;
+
+ const int one_second = 1000000000;
+ const time_t sec = ns / one_second;
+ struct tm tm;
+
+ // Supported on bionic, glibc, and macOS, but not mingw.
+ if (localtime_r(&sec, &tm) == NULL) {
+ buffer[0] = '\0';
+ return;
+ }
+
+ switch (format) {
+ default:
+ case kPrintFormatLong:
+ if (snprintf(buffer, bufferSize, "%02d-%02d %02d:%02d:%02d.%03d",
+ tm.tm_mon + 1, // localtime_r uses months in 0 - 11 range
+ tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
+ (int)(ns % one_second / 1000000)) < 0) {
+ buffer[0] = '\0'; // null terminate on format error, which should not happen
+ }
+ break;
+ case kPrintFormatShort:
+ if (snprintf(buffer, bufferSize, "%02d:%02d:%02d.%03d",
+ tm.tm_hour, tm.tm_min, tm.tm_sec,
+ (int)(ns % one_second / 1000000)) < 0) {
+ buffer[0] = '\0'; // null terminate on format error, which should not happen
+ }
+ break;
+ }
+}
+
+std::string mediametrics::Item::toString() const {
+ std::string result;
+ char buffer[kMaxPropertyStringSize];
+
+ snprintf(buffer, sizeof(buffer), "{%s, (%s), (%s, %d, %d)",
+ mKey.c_str(),
+ timeStringFromNs(mTimestamp, kPrintFormatLong).time,
+ mPkgName.c_str(), mPid, mUid
+ );
+ result.append(buffer);
+ bool first = true;
+ for (auto &prop : *this) {
+ prop.toStringBuffer(buffer, sizeof(buffer));
+ result += first ? ", (" : ", ";
+ result += buffer;
+ first = false;
+ }
+ result.append(")}");
+ return result;
+}
+
+// for the lazy, we offer methods that finds the service and
+// calls the appropriate daemon
+bool mediametrics::Item::selfrecord() {
+ ALOGD_IF(DEBUG_API, "%s: delivering %s", __func__, this->toString().c_str());
+ sp<IMediaMetricsService> svc = getService();
+ if (svc != NULL) {
+ status_t status = svc->submit(this);
+ if (status != NO_ERROR) {
+ ALOGW("%s: failed to record: %s", __func__, this->toString().c_str());
+ return false;
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+//static
+bool BaseItem::isEnabled() {
+ // completely skip logging from certain UIDs. We do this here
+ // to avoid the multi-second timeouts while we learn that
+ // sepolicy will not let us find the service.
+ // We do this only for a select set of UIDs
+ // The sepolicy protection is still in place, we just want a faster
+ // response from this specific, small set of uids.
+
+ // This is checked only once in the lifetime of the process.
+ const uid_t uid = getuid();
+ switch (uid) {
+ case AID_RADIO: // telephony subsystem, RIL
+ return false;
+ }
+
+ int enabled = property_get_int32(Item::EnabledProperty, -1);
+ if (enabled == -1) {
+ enabled = property_get_int32(Item::EnabledPropertyPersist, -1);
+ }
+ if (enabled == -1) {
+ enabled = Item::EnabledProperty_default;
+ }
+ return enabled > 0;
+}
+
+// monitor health of our connection to the metrics service
+class MediaMetricsDeathNotifier : public IBinder::DeathRecipient {
+ virtual void binderDied(const wp<IBinder> &) {
+ ALOGW("Reacquire service connection on next request");
+ BaseItem::dropInstance();
+ }
+};
+
+static sp<MediaMetricsDeathNotifier> sNotifier;
+// static
+sp<IMediaMetricsService> BaseItem::sMediaMetricsService;
+static std::mutex sServiceMutex;
+static int sRemainingBindAttempts = SVC_TRIES;
+
+// static
+void BaseItem::dropInstance() {
+ std::lock_guard _l(sServiceMutex);
+ sRemainingBindAttempts = SVC_TRIES;
+ sMediaMetricsService = nullptr;
+}
+
+// static
+bool BaseItem::submitBuffer(const char *buffer, size_t size) {
+/*
+ mediametrics::Item item;
+ status_t status = item.readFromByteString(buffer, size);
+ ALOGD("%s: status:%d, size:%zu, item:%s", __func__, status, size, item.toString().c_str());
+ return item.selfrecord();
+ */
+
+ ALOGD_IF(DEBUG_API, "%s: delivering %zu bytes", __func__, size);
+ sp<IMediaMetricsService> svc = getService();
+ if (svc != nullptr) {
+ const status_t status = svc->submitBuffer(buffer, size);
+ if (status != NO_ERROR) {
+ ALOGW("%s: failed(%d) to record: %zu bytes", __func__, status, size);
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
+
+//static
+sp<IMediaMetricsService> BaseItem::getService() {
+ static const char *servicename = "media.metrics";
+ static const bool enabled = isEnabled(); // singleton initialized
+
+ if (enabled == false) {
+ ALOGD_IF(DEBUG_SERVICEACCESS, "disabled");
+ return nullptr;
+ }
+ std::lock_guard _l(sServiceMutex);
+ // think of remainingBindAttempts as telling us whether service == nullptr because
+ // (1) we haven't tried to initialize it yet
+ // (2) we've tried to initialize it, but failed.
+ if (sMediaMetricsService == nullptr && sRemainingBindAttempts > 0) {
+ const char *badness = "";
+ sp<IServiceManager> sm = defaultServiceManager();
+ if (sm != nullptr) {
+ sp<IBinder> binder = sm->getService(String16(servicename));
+ if (binder != nullptr) {
+ sMediaMetricsService = interface_cast<IMediaMetricsService>(binder);
+ sNotifier = new MediaMetricsDeathNotifier();
+ binder->linkToDeath(sNotifier);
+ } else {
+ badness = "did not find service";
+ }
+ } else {
+ badness = "No Service Manager access";
+ }
+ if (sMediaMetricsService == nullptr) {
+ if (sRemainingBindAttempts > 0) {
+ sRemainingBindAttempts--;
+ }
+ ALOGD_IF(DEBUG_SERVICEACCESS, "%s: unable to bind to service %s: %s",
+ __func__, servicename, badness);
+ }
+ }
+ return sMediaMetricsService;
+}
+
+
+status_t mediametrics::Item::writeToByteString(char **pbuffer, size_t *plength) const
+{
+ if (pbuffer == nullptr || plength == nullptr)
+ return BAD_VALUE;
+
+ // get size
+ const size_t keySizeZeroTerminated = strlen(mKey.c_str()) + 1;
+ if (keySizeZeroTerminated > UINT16_MAX) {
+ ALOGW("%s: key size %zu too large", __func__, keySizeZeroTerminated);
+ return INVALID_OPERATION;
+ }
+ const uint16_t version = 0;
+ const uint32_t header_size =
+ sizeof(uint32_t) // total size
+ + sizeof(header_size) // header size
+ + sizeof(version) // encoding version
+ + sizeof(uint16_t) // key size
+ + keySizeZeroTerminated // key, zero terminated
+ + sizeof(int32_t) // pid
+ + sizeof(int32_t) // uid
+ + sizeof(int64_t) // timestamp
+ ;
+
+ uint32_t size = header_size
+ + sizeof(uint32_t) // # properties
+ ;
+ for (auto &prop : *this) {
+ const size_t propSize = prop.getByteStringSize();
+ if (propSize > UINT16_MAX) {
+ ALOGW("%s: prop %s size %zu too large", __func__, prop.getName(), propSize);
+ return INVALID_OPERATION;
+ }
+ if (__builtin_add_overflow(size, propSize, &size)) {
+ ALOGW("%s: item size overflow at property %s", __func__, prop.getName());
+ return INVALID_OPERATION;
+ }
+ }
+
+ // since we fill every byte in the buffer (there is no padding),
+ // malloc is used here instead of calloc.
+ char * const build = (char *)malloc(size);
+ if (build == nullptr) return NO_MEMORY;
+
+ char *filling = build;
+ char *buildmax = build + size;
+ if (insert((uint32_t)size, &filling, buildmax) != NO_ERROR
+ || insert(header_size, &filling, buildmax) != NO_ERROR
+ || insert(version, &filling, buildmax) != NO_ERROR
+ || insert((uint16_t)keySizeZeroTerminated, &filling, buildmax) != NO_ERROR
+ || insert(mKey.c_str(), &filling, buildmax) != NO_ERROR
+ || insert((int32_t)mPid, &filling, buildmax) != NO_ERROR
+ || insert((int32_t)mUid, &filling, buildmax) != NO_ERROR
+ || insert((int64_t)mTimestamp, &filling, buildmax) != NO_ERROR
+ || insert((uint32_t)mProps.size(), &filling, buildmax) != NO_ERROR) {
+ ALOGE("%s:could not write header", __func__); // shouldn't happen
+ free(build);
+ return INVALID_OPERATION;
+ }
+ for (auto &prop : *this) {
+ if (prop.writeToByteString(&filling, buildmax) != NO_ERROR) {
+ free(build);
+ // shouldn't happen
+ ALOGE("%s:could not write prop %s", __func__, prop.getName());
+ return INVALID_OPERATION;
+ }
+ }
+
+ if (filling != buildmax) {
+ ALOGE("%s: problems populating; wrote=%d planned=%d",
+ __func__, (int)(filling - build), (int)size);
+ free(build);
+ return INVALID_OPERATION;
+ }
+ *pbuffer = build;
+ *plength = size;
+ return NO_ERROR;
+}
+
+status_t mediametrics::Item::readFromByteString(const char *bufferptr, size_t length)
+{
+ if (bufferptr == nullptr) return BAD_VALUE;
+
+ const char *read = bufferptr;
+ const char *readend = bufferptr + length;
+
+ uint32_t size;
+ uint32_t header_size;
+ uint16_t version;
+ uint16_t key_size;
+ std::string key;
+ int32_t pid;
+ int32_t uid;
+ int64_t timestamp;
+ uint32_t propCount;
+ if (extract(&size, &read, readend) != NO_ERROR
+ || extract(&header_size, &read, readend) != NO_ERROR
+ || extract(&version, &read, readend) != NO_ERROR
+ || extract(&key_size, &read, readend) != NO_ERROR
+ || extract(&key, &read, readend) != NO_ERROR
+ || extract(&pid, &read, readend) != NO_ERROR
+ || extract(&uid, &read, readend) != NO_ERROR
+ || extract(×tamp, &read, readend) != NO_ERROR
+ || size > length
+ || key.size() + 1 != key_size
+ || header_size > size) {
+ ALOGW("%s: invalid header", __func__);
+ return INVALID_OPERATION;
+ }
+ mKey = std::move(key);
+ const size_t pos = read - bufferptr;
+ if (pos > header_size) {
+ ALOGW("%s: invalid header pos:%zu > header_size:%u",
+ __func__, pos, header_size);
+ return INVALID_OPERATION;
+ } else if (pos < header_size) {
+ ALOGW("%s: mismatched header pos:%zu < header_size:%u, advancing",
+ __func__, pos, header_size);
+ read += (header_size - pos);
+ }
+ if (extract(&propCount, &read, readend) != NO_ERROR) {
+ ALOGD("%s: cannot read prop count", __func__);
+ return INVALID_OPERATION;
+ }
+ mPid = pid;
+ mUid = uid;
+ mTimestamp = timestamp;
+ for (size_t i = 0; i < propCount; ++i) {
+ Prop prop;
+ if (prop.readFromByteString(&read, readend) != NO_ERROR) {
+ ALOGW("%s: cannot read prop %zu", __func__, i);
+ return INVALID_OPERATION;
+ }
+ mProps[prop.getName()] = std::move(prop);
+ }
+ return NO_ERROR;
+}
+
+status_t mediametrics::Item::Prop::readFromParcel(const Parcel& data)
+{
+ const char *key = data.readCString();
+ if (key == nullptr) return BAD_VALUE;
+ int32_t type;
+ status_t status = data.readInt32(&type);
+ if (status != NO_ERROR) return status;
+ switch (type) {
+ case mediametrics::kTypeInt32: {
+ int32_t value;
+ status = data.readInt32(&value);
+ if (status != NO_ERROR) return status;
+ mElem = value;
+ } break;
+ case mediametrics::kTypeInt64: {
+ int64_t value;
+ status = data.readInt64(&value);
+ if (status != NO_ERROR) return status;
+ mElem = value;
+ } break;
+ case mediametrics::kTypeDouble: {
+ double value;
+ status = data.readDouble(&value);
+ if (status != NO_ERROR) return status;
+ mElem = value;
+ } break;
+ case mediametrics::kTypeCString: {
+ const char *s = data.readCString();
+ if (s == nullptr) return BAD_VALUE;
+ mElem = s;
+ } break;
+ case mediametrics::kTypeRate: {
+ std::pair<int64_t, int64_t> rate;
+ status = data.readInt64(&rate.first)
+ ?: data.readInt64(&rate.second);
+ if (status != NO_ERROR) return status;
+ mElem = rate;
+ } break;
+ case mediametrics::kTypeNone: {
+ mElem = std::monostate{};
+ } break;
+ default:
+ ALOGE("%s: reading bad item type: %d", __func__, type);
+ return BAD_VALUE;
+ }
+ setName(key);
+ return NO_ERROR;
+}
+
+status_t mediametrics::Item::Prop::readFromByteString(
+ const char **bufferpptr, const char *bufferptrmax)
+{
+ uint16_t len;
+ std::string name;
+ uint8_t type;
+ status_t status = extract(&len, bufferpptr, bufferptrmax)
+ ?: extract(&type, bufferpptr, bufferptrmax)
+ ?: extract(&name, bufferpptr, bufferptrmax);
+ if (status != NO_ERROR) return status;
+ switch (type) {
+ case mediametrics::kTypeInt32: {
+ int32_t value;
+ status = extract(&value, bufferpptr, bufferptrmax);
+ if (status != NO_ERROR) return status;
+ mElem = value;
+ } break;
+ case mediametrics::kTypeInt64: {
+ int64_t value;
+ status = extract(&value, bufferpptr, bufferptrmax);
+ if (status != NO_ERROR) return status;
+ mElem = value;
+ } break;
+ case mediametrics::kTypeDouble: {
+ double value;
+ status = extract(&value, bufferpptr, bufferptrmax);
+ if (status != NO_ERROR) return status;
+ mElem = value;
+ } break;
+ case mediametrics::kTypeRate: {
+ std::pair<int64_t, int64_t> value;
+ status = extract(&value.first, bufferpptr, bufferptrmax)
+ ?: extract(&value.second, bufferpptr, bufferptrmax);
+ if (status != NO_ERROR) return status;
+ mElem = value;
+ } break;
+ case mediametrics::kTypeCString: {
+ std::string value;
+ status = extract(&value, bufferpptr, bufferptrmax);
+ if (status != NO_ERROR) return status;
+ mElem = std::move(value);
+ } break;
+ case mediametrics::kTypeNone: {
+ mElem = std::monostate{};
+ } break;
+ default:
+ ALOGE("%s: found bad prop type: %d, name %s",
+ __func__, (int)type, mName.c_str()); // no payload sent
+ return BAD_VALUE;
+ }
+ mName = name;
+ return NO_ERROR;
+}
+
+} // namespace android::mediametrics
diff --git a/media/libmediametrics/include/MediaAnalyticsItem.h b/media/libmediametrics/include/MediaAnalyticsItem.h
deleted file mode 100644
index bff7c9b..0000000
--- a/media/libmediametrics/include/MediaAnalyticsItem.h
+++ /dev/null
@@ -1,943 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_MEDIA_MEDIAANALYTICSITEM_H
-#define ANDROID_MEDIA_MEDIAANALYTICSITEM_H
-
-#include "MediaMetrics.h"
-
-#include <algorithm>
-#include <string>
-#include <sys/types.h>
-
-#include <cutils/properties.h>
-#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <utils/RefBase.h>
-#include <utils/StrongPointer.h>
-#include <utils/Timers.h>
-
-namespace android {
-
-class IMediaAnalyticsService;
-class Parcel;
-
-/*
- * MediaMetrics Item
- *
- * Byte string format.
- *
- * For Java
- * int64 corresponds to long
- * int32, uint32 corresponds to int
- * uint16 corresponds to char
- * uint8, int8 corresponds to byte
- *
- * Hence uint8 and uint32 values are limited to INT8_MAX and INT32_MAX.
- *
- * Physical layout of integers and doubles within the MediaMetrics byte string
- * is in Native / host order, which is nearly always little endian.
- *
- * -- begin of item
- * -- begin of header
- * (uint32) item size: including the item size field
- * (uint32) header size, including the item size and header size fields.
- * (uint16) version: exactly 0
- * (uint16) key size, that is key strlen + 1 for zero termination.
- * (int8)+ key string which is 0 terminated
- * (int32) pid
- * (int32) uid
- * (int64) timestamp
- * -- end of header
- * -- begin body
- * (uint32) number of properties
- * -- repeat for number of properties
- * (uint16) property size, including property size field itself
- * (uint8) type of property
- * (int8)+ key string, including 0 termination
- * based on type of property (given above), one of:
- * (int32)
- * (int64)
- * (double)
- * (int8)+ for cstring, including 0 termination
- * (int64, int64) for rate
- * -- end body
- * -- end of item
- */
-
-namespace mediametrics {
-
-// Type must match MediaMetrics.java
-enum Type {
- kTypeNone = 0,
- kTypeInt32 = 1,
- kTypeInt64 = 2,
- kTypeDouble = 3,
- kTypeCString = 4,
- kTypeRate = 5,
-};
-
-template<size_t N>
-static inline bool startsWith(const std::string &s, const char (&comp)[N]) {
- return !strncmp(s.c_str(), comp, N-1);
-}
-
-/**
- * Media Metrics BaseItem
- *
- * A base class which contains utility static functions to write to a byte stream
- * and access the Media Metrics service.
- */
-
-class BaseItem {
- friend class MediaMetricsDeathNotifier; // for dropInstance
- // enabled 1, disabled 0
-public:
- // are we collecting analytics data
- static bool isEnabled();
-
-protected:
- static constexpr const char * const EnabledProperty = "media.metrics.enabled";
- static constexpr const char * const EnabledPropertyPersist = "persist.media.metrics.enabled";
- static const int EnabledProperty_default = 1;
-
- // let's reuse a binder connection
- static sp<IMediaAnalyticsService> sAnalyticsService;
- static sp<IMediaAnalyticsService> getInstance();
- static void dropInstance();
- static bool submitBuffer(const char *buffer, size_t len);
-
- static status_t writeToByteString(
- const char *name, int32_t value, char **bufferpptr, char *bufferptrmax);
- static status_t writeToByteString(
- const char *name, int64_t value, char **bufferpptr, char *bufferptrmax);
- static status_t writeToByteString(
- const char *name, double value, char **bufferpptr, char *bufferptrmax);
- static status_t writeToByteString(
- const char *name, const std::pair<int64_t, int64_t> &value,
- char **bufferpptr, char *bufferptrmax);
- static status_t writeToByteString(
- const char *name, char * const &value, char **bufferpptr, char *bufferptrmax);
- static status_t writeToByteString(
- const char *name, const char * const &value, char **bufferpptr, char *bufferptrmax);
- struct none_t {}; // for kTypeNone
- static status_t writeToByteString(
- const char *name, const none_t &, char **bufferpptr, char *bufferptrmax);
-
- template<typename T>
- static status_t sizeOfByteString(const char *name, const T& value) {
- return 2 + 1 + strlen(name) + 1 + sizeof(value);
- }
- template<> // static
- status_t sizeOfByteString(const char *name, char * const &value) {
- return 2 + 1 + strlen(name) + 1 + strlen(value) + 1;
- }
- template<> // static
- status_t sizeOfByteString(const char *name, const char * const &value) {
- return 2 + 1 + strlen(name) + 1 + strlen(value) + 1;
- }
- template<> // static
- status_t sizeOfByteString(const char *name, const none_t &) {
- return 2 + 1 + strlen(name) + 1;
- }
-};
-
-/**
- * Media Metrics BufferedItem
- *
- * A base class which represents a put-only Media Metrics item, storing
- * the Media Metrics data in a buffer with begin and end pointers.
- *
- * If a property key is entered twice, it will be stored in the buffer twice,
- * and (implementation defined) the last value for that key will be used
- * by the Media Metrics service.
- *
- * For realloc, a baseRealloc pointer must be passed in either explicitly
- * or implicitly in the constructor. This will be updated with the value used on realloc.
- */
-class BufferedItem : public BaseItem {
-public:
- static inline constexpr uint16_t kVersion = 0;
-
- virtual ~BufferedItem() = default;
- BufferedItem(const BufferedItem&) = delete;
- BufferedItem& operator=(const BufferedItem&) = delete;
-
- BufferedItem(const std::string key, char *begin, char *end)
- : BufferedItem(key.c_str(), begin, end) { }
-
- BufferedItem(const char *key, char *begin, char *end)
- : BufferedItem(key, begin, end, nullptr) { }
-
- BufferedItem(const char *key, char **begin, char *end)
- : BufferedItem(key, *begin, end, begin) { }
-
- BufferedItem(const char *key, char *begin, char *end, char **baseRealloc)
- : mBegin(begin)
- , mEnd(end)
- , mBaseRealloc(baseRealloc)
- {
- init(key);
- }
-
- template<typename T>
- BufferedItem &set(const char *key, const T& value) {
- reallocFor(sizeOfByteString(key, value));
- if (mStatus == NO_ERROR) {
- mStatus = BaseItem::writeToByteString(key, value, &mBptr, mEnd);
- ++mPropCount;
- }
- return *this;
- }
-
- template<typename T>
- BufferedItem &set(const std::string& key, const T& value) {
- return set(key.c_str(), value);
- }
-
- BufferedItem &setPid(pid_t pid) {
- if (mStatus == NO_ERROR) {
- copyTo(mBegin + mHeaderLen - 16, (int32_t)pid);
- }
- return *this;
- }
-
- BufferedItem &setUid(uid_t uid) {
- if (mStatus == NO_ERROR) {
- copyTo(mBegin + mHeaderLen - 12, (int32_t)uid);
- }
- return *this;
- }
-
- BufferedItem &setTimestamp(nsecs_t timestamp) {
- if (mStatus == NO_ERROR) {
- copyTo(mBegin + mHeaderLen - 8, (int64_t)timestamp);
- }
- return *this;
- }
-
- bool record() {
- return updateHeader()
- && BaseItem::submitBuffer(getBuffer(), getLength());
- }
-
- bool isValid () const {
- return mStatus == NO_ERROR;
- }
-
- char *getBuffer() const { return mBegin; }
- size_t getLength() const { return mBptr - mBegin; }
- size_t getRemaining() const { return mEnd - mBptr; }
- size_t getCapacity() const { return mEnd - mBegin; }
-
- bool updateHeader() {
- if (mStatus != NO_ERROR) return false;
- copyTo(mBegin + 0, (uint32_t)getLength());
- copyTo(mBegin + 4, (uint32_t)mHeaderLen);
- copyTo(mBegin + mHeaderLen, (uint32_t)mPropCount);
- return true;
- }
-
-protected:
- BufferedItem() = default;
-
- void reallocFor(size_t required) {
- if (mStatus != NO_ERROR) return;
- const size_t remaining = getRemaining();
- if (required <= remaining) return;
- if (mBaseRealloc == nullptr) {
- mStatus = NO_MEMORY;
- return;
- }
-
- const size_t current = getLength();
- size_t minimum = current + required;
- if (minimum > SSIZE_MAX >> 1) {
- mStatus = NO_MEMORY;
- return;
- }
- minimum <<= 1;
- void *newptr = realloc(*mBaseRealloc, minimum);
- if (newptr == nullptr) {
- mStatus = NO_MEMORY;
- return;
- }
- if (newptr != *mBaseRealloc) {
- // ALOGD("base changed! current:%zu new size %zu", current, minimum);
- if (*mBaseRealloc == nullptr) {
- memcpy(newptr, mBegin, current);
- }
- mBegin = (char *)newptr;
- *mBaseRealloc = mBegin;
- mEnd = mBegin + minimum;
- mBptr = mBegin + current;
- } else {
- // ALOGD("base kept! current:%zu new size %zu", current, minimum);
- mEnd = mBegin + minimum;
- }
- }
- template<typename T>
- void copyTo(char *ptr, const T& value) {
- memcpy(ptr, &value, sizeof(value));
- }
-
- void init(const char *key) {
- mBptr = mBegin;
- const size_t keylen = strlen(key) + 1;
- mHeaderLen = 4 + 4 + 2 + 2 + keylen + 4 + 4 + 8;
- reallocFor(mHeaderLen);
- if (mStatus != NO_ERROR) return;
- mBptr = mBegin + mHeaderLen + 4; // this includes propcount.
-
- if (mEnd < mBptr || keylen > UINT16_MAX) {
- mStatus = NO_MEMORY;
- mBptr = mEnd;
- return;
- }
- copyTo(mBegin + 8, kVersion);
- copyTo(mBegin + 10, (uint16_t)keylen);
- strcpy(mBegin + 12, key);
-
- // initialize some parameters (that could be overridden)
- setPid(-1);
- setUid(-1);
- setTimestamp(0);
- }
-
- char *mBegin = nullptr;
- char *mEnd = nullptr;
- char **mBaseRealloc = nullptr; // set to an address if realloc should be done.
- // upon return, that pointer is updated with
- // whatever needs to be freed.
- char *mBptr = nullptr;
- status_t mStatus = NO_ERROR;
- uint32_t mPropCount = 0;
- uint32_t mHeaderLen = 0;
-};
-
-/**
- * MediaMetrics Item is a stack allocated media analytics item used for
- * fast logging. It falls over to a malloc if needed.
- *
- * This is templated with a buffer size to allocate on the stack.
- */
-template <size_t N = 4096>
-class Item : public BufferedItem {
-public:
- explicit Item(const std::string key) : Item(key.c_str()) { }
-
- // Since this class will not be defined before the base class, we initialize variables
- // in our own order.
- explicit Item(const char *key) {
- mBegin = mBuffer;
- mEnd = mBuffer + N;
- mBaseRealloc = &mReallocPtr;
- init(key);
- }
-
- ~Item() override {
- if (mReallocPtr != nullptr) { // do the check before calling free to avoid overhead.
- free(mReallocPtr);
- }
- }
-
-private:
- char *mReallocPtr = nullptr; // set non-null by base class if realloc happened.
- char mBuffer[N];
-};
-
-} // mediametrics
-
-/**
- * Media Metrics MediaAnalyticsItem
- *
- * A mutable item representing an event or record that will be
- * logged with the Media Metrics service. For client logging, one should
- * use the mediametrics::Item.
- *
- * The MediaAnalyticsItem is designed for the service as it has getters.
- */
-class MediaAnalyticsItem : public mediametrics::BaseItem {
- friend class MediaMetricsJNI; // TODO: remove this access
-
-public:
-
- // TODO: remove this duplicate definition when frameworks base is updated.
- enum Type {
- kTypeNone = 0,
- kTypeInt32 = 1,
- kTypeInt64 = 2,
- kTypeDouble = 3,
- kTypeCString = 4,
- kTypeRate = 5,
- };
-
- static constexpr const char * const kKeyNone = "none";
- static constexpr const char * const kKeyAny = "any";
-
- enum {
- PROTO_V0 = 0,
- PROTO_FIRST = PROTO_V0,
- PROTO_V1 = 1,
- PROTO_LAST = PROTO_V1,
- };
-
- // T must be convertible to mKey
- template <typename T>
- explicit MediaAnalyticsItem(T key)
- : mKey(key) { }
- MediaAnalyticsItem() = default;
-
- MediaAnalyticsItem(const MediaAnalyticsItem&) = delete;
- MediaAnalyticsItem &operator=(const MediaAnalyticsItem&) = delete;
-
- bool operator==(const MediaAnalyticsItem& other) const {
- if (mPropCount != other.mPropCount
- || mPid != other.mPid
- || mUid != other.mUid
- || mPkgName != other.mPkgName
- || mPkgVersionCode != other.mPkgVersionCode
- || mKey != other.mKey
- || mTimestamp != other.mTimestamp) return false;
- for (size_t i = 0; i < mPropCount; ++i) {
- Prop *p = other.findProp(mProps[i].getName());
- if (p == nullptr || mProps[i] != *p) return false;
- }
- return true;
- }
- bool operator!=(const MediaAnalyticsItem& other) const {
- return !(*this == other);
- }
-
- template <typename T>
- static MediaAnalyticsItem* create(T key) {
- return new MediaAnalyticsItem(key);
- }
- static MediaAnalyticsItem* create() {
- return new MediaAnalyticsItem();
- }
-
- static MediaAnalyticsItem* convert(mediametrics_handle_t);
- static mediametrics_handle_t convert(MediaAnalyticsItem *);
-
- // access functions for the class
- ~MediaAnalyticsItem();
-
- // reset all contents, discarding any extra data
- void clear();
- MediaAnalyticsItem *dup();
-
- MediaAnalyticsItem &setKey(const char *key) {
- mKey = key;
- return *this;
- }
- const std::string& getKey() const { return mKey; }
-
- // # of properties in the record
- size_t count() const { return mPropCount; }
-
- template<typename S, typename T>
- MediaAnalyticsItem &set(S key, T value) {
- allocateProp(key)->set(value);
- return *this;
- }
-
- // set values appropriately
- MediaAnalyticsItem &setInt32(const char *key, int32_t value) {
- return set(key, value);
- }
- MediaAnalyticsItem &setInt64(const char *key, int64_t value) {
- return set(key, value);
- }
- MediaAnalyticsItem &setDouble(const char *key, double value) {
- return set(key, value);
- }
- MediaAnalyticsItem &setRate(const char *key, int64_t count, int64_t duration) {
- return set(key, std::make_pair(count, duration));
- }
- MediaAnalyticsItem &setCString(const char *key, const char *value) {
- return set(key, value);
- }
-
- // fused get/add/set; if attr wasn't there, it's a simple set.
- // type-mismatch counts as "wasn't there".
- template<typename S, typename T>
- MediaAnalyticsItem &add(S key, T value) {
- allocateProp(key)->add(value);
- return *this;
- }
-
- MediaAnalyticsItem &addInt32(const char *key, int32_t value) {
- return add(key, value);
- }
- MediaAnalyticsItem &addInt64(const char *key, int64_t value) {
- return add(key, value);
- }
- MediaAnalyticsItem &addDouble(const char *key, double value) {
- return add(key, value);
- }
- MediaAnalyticsItem &addRate(const char *key, int64_t count, int64_t duration) {
- return add(key, std::make_pair(count, duration));
- }
-
- // find & extract values
- // return indicates whether attr exists (and thus value filled in)
- // NULL parameter value suppresses storage of value.
- template<typename S, typename T>
- bool get(S key, T *value) const {
- Prop *prop = findProp(key);
- return prop != nullptr && prop->get(value);
- }
-
- bool getInt32(const char *key, int32_t *value) const {
- return get(key, value);
- }
- bool getInt64(const char *key, int64_t *value) const {
- return get(key, value);
- }
- bool getDouble(const char *key, double *value) const {
- return get(key, value);
- }
- bool getRate(const char *key, int64_t *count, int64_t *duration, double *rate) const {
- std::pair<int64_t, int64_t> value;
- if (!get(key, &value)) return false;
- if (count != nullptr) *count = value.first;
- if (duration != nullptr) *duration = value.second;
- if (rate != nullptr) {
- if (value.second != 0) {
- *rate = (double)value.first / value.second; // TODO: isn't INF OK?
- } else {
- *rate = 0.;
- }
- }
- return true;
- }
- // Caller owns the returned string
- bool getCString(const char *key, char **value) const {
- const char *cs;
- if (get(key, &cs)) {
- *value = cs != nullptr ? strdup(cs) : nullptr;
- return true;
- }
- return false;
- }
- bool getString(const char *key, std::string *value) const {
- return get(key, value);
- }
-
- // Deliver the item to MediaMetrics
- bool selfrecord();
-
- // remove indicated attributes and their values
- // filterNot() could also be called keepOnly()
- // return value is # attributes removed
- // XXX: perhaps 'remove' instead of 'filter'
- // XXX: filterNot would become 'keep'
- size_t filter(size_t count, const char *attrs[]);
- size_t filterNot(size_t count, const char *attrs[]);
- size_t filter(const char *attr) { return filter(1, &attr); }
-
- // below here are used on server side or to talk to server
- // clients need not worry about these.
-
- // timestamp, pid, and uid only used on server side
- // timestamp is in 'nanoseconds, unix time'
- MediaAnalyticsItem &setTimestamp(nsecs_t);
- nsecs_t getTimestamp() const;
-
- MediaAnalyticsItem &setPid(pid_t);
- pid_t getPid() const;
-
- MediaAnalyticsItem &setUid(uid_t);
- uid_t getUid() const;
-
- MediaAnalyticsItem &setPkgName(const std::string &pkgName);
- std::string getPkgName() const { return mPkgName; }
-
- MediaAnalyticsItem &setPkgVersionCode(int64_t);
- int64_t getPkgVersionCode() const;
-
- // our serialization code for binder calls
- status_t writeToParcel(Parcel *) const;
- status_t readFromParcel(const Parcel&);
-
- status_t writeToByteString(char **bufferptr, size_t *length) const;
- status_t readFromByteString(const char *bufferptr, size_t length);
-
-
- std::string toString() const;
- std::string toString(int version) const;
- const char *toCString();
- const char *toCString(int version);
-
- protected:
-
- // merge fields from arg into this
- // with rules for first/last/add, etc
- // XXX: document semantics and how they are indicated
- // caller continues to own 'incoming'
- bool merge(MediaAnalyticsItem *incoming);
-
-private:
- // handle Parcel version 0
- int32_t writeToParcel0(Parcel *) const;
- int32_t readFromParcel0(const Parcel&);
-
-
-
- // checks equality even with nullptr.
- static bool stringEquals(const char *a, const char *b) {
- if (a == nullptr) {
- return b == nullptr;
- } else {
- return b != nullptr && strcmp(a, b) == 0;
- }
- }
-
-public:
-
- class Prop {
- friend class MediaMetricsJNI; // TODO: remove this access
- public:
- Prop() = default;
- Prop(const Prop& other) {
- *this = other;
- }
- Prop& operator=(const Prop& other) {
- if (other.mName != nullptr) {
- mName = strdup(other.mName);
- } else {
- mName = nullptr;
- }
- mType = other.mType;
- switch (mType) {
- case kTypeInt32:
- u.int32Value = other.u.int32Value;
- break;
- case kTypeInt64:
- u.int64Value = other.u.int64Value;
- break;
- case kTypeDouble:
- u.doubleValue = other.u.doubleValue;
- break;
- case kTypeCString:
- u.CStringValue = strdup(other.u.CStringValue);
- break;
- case kTypeRate:
- u.rate = other.u.rate;
- break;
- case kTypeNone:
- break;
- default:
- // abort?
- break;
- }
- return *this;
- }
- bool operator==(const Prop& other) const {
- if (!stringEquals(mName, other.mName)
- || mType != other.mType) return false;
- switch (mType) {
- case kTypeInt32:
- return u.int32Value == other.u.int32Value;
- case kTypeInt64:
- return u.int64Value == other.u.int64Value;
- case kTypeDouble:
- return u.doubleValue == other.u.doubleValue;
- case kTypeCString:
- return stringEquals(u.CStringValue, other.u.CStringValue);
- case kTypeRate:
- return u.rate == other.u.rate;
- case kTypeNone:
- default:
- return true;
- }
- }
- bool operator!=(const Prop& other) const {
- return !(*this == other);
- }
-
- void clear() {
- free(mName);
- mName = nullptr;
- clearValue();
- }
- void clearValue() {
- if (mType == kTypeCString) {
- free(u.CStringValue);
- u.CStringValue = nullptr;
- }
- mType = kTypeNone;
- }
-
- Type getType() const {
- return mType;
- }
-
- const char *getName() const {
- return mName;
- }
-
- void swap(Prop& other) {
- std::swap(mName, other.mName);
- std::swap(mType, other.mType);
- std::swap(u, other.u);
- }
-
- void setName(const char *name) {
- free(mName);
- if (name != nullptr) {
- mName = strdup(name);
- } else {
- mName = nullptr;
- }
- }
-
- bool isNamed(const char *name) const {
- return stringEquals(name, mName);
- }
-
- template <typename T> void visit(T f) const {
- switch (mType) {
- case MediaAnalyticsItem::kTypeInt32:
- f(u.int32Value);
- return;
- case MediaAnalyticsItem::kTypeInt64:
- f(u.int64Value);
- return;
- case MediaAnalyticsItem::kTypeDouble:
- f(u.doubleValue);
- return;
- case MediaAnalyticsItem::kTypeRate:
- f(u.rate);
- return;
- case MediaAnalyticsItem::kTypeCString:
- f(u.CStringValue);
- return;
- default:
- return;
- }
- }
-
- template <typename T> bool get(T *value) const = delete;
- template <>
- bool get(int32_t *value) const {
- if (mType != kTypeInt32) return false;
- if (value != nullptr) *value = u.int32Value;
- return true;
- }
- template <>
- bool get(int64_t *value) const {
- if (mType != kTypeInt64) return false;
- if (value != nullptr) *value = u.int64Value;
- return true;
- }
- template <>
- bool get(double *value) const {
- if (mType != kTypeDouble) return false;
- if (value != nullptr) *value = u.doubleValue;
- return true;
- }
- template <>
- bool get(const char** value) const {
- if (mType != kTypeCString) return false;
- if (value != nullptr) *value = u.CStringValue;
- return true;
- }
- template <>
- bool get(std::string* value) const {
- if (mType != kTypeCString) return false;
- if (value != nullptr) *value = u.CStringValue;
- return true;
- }
- template <>
- bool get(std::pair<int64_t, int64_t> *value) const {
- if (mType != kTypeRate) return false;
- if (value != nullptr) {
- *value = u.rate;
- }
- return true;
- }
-
- template <typename T> void set(const T& value) = delete;
- template <>
- void set(const int32_t& value) {
- mType = kTypeInt32;
- u.int32Value = value;
- }
- template <>
- void set(const int64_t& value) {
- mType = kTypeInt64;
- u.int64Value = value;
- }
- template <>
- void set(const double& value) {
- mType = kTypeDouble;
- u.doubleValue = value;
- }
- template <>
- void set(const char* const& value) {
- if (mType == kTypeCString) {
- free(u.CStringValue);
- } else {
- mType = kTypeCString;
- }
- if (value == nullptr) {
- u.CStringValue = nullptr;
- } else {
- size_t len = strlen(value);
- if (len > UINT16_MAX - 1) {
- len = UINT16_MAX - 1;
- }
- u.CStringValue = (char *)malloc(len + 1);
- strncpy(u.CStringValue, value, len);
- u.CStringValue[len] = 0;
- }
- }
- template <>
- void set(const std::pair<int64_t, int64_t> &value) {
- mType = kTypeRate;
- u.rate = {value.first, value.second};
- }
-
- template <typename T> void add(const T& value) = delete;
- template <>
- void add(const int32_t& value) {
- if (mType == kTypeInt32) {
- u.int32Value += value;
- } else {
- mType = kTypeInt32;
- u.int32Value = value;
- }
- }
- template <>
- void add(const int64_t& value) {
- if (mType == kTypeInt64) {
- u.int64Value += value;
- } else {
- mType = kTypeInt64;
- u.int64Value = value;
- }
- }
- template <>
- void add(const double& value) {
- if (mType == kTypeDouble) {
- u.doubleValue += value;
- } else {
- mType = kTypeDouble;
- u.doubleValue = value;
- }
- }
- template <>
- void add(const std::pair<int64_t, int64_t>& value) {
- if (mType == kTypeRate) {
- u.rate.first += value.first;
- u.rate.second += value.second;
- } else {
- mType = kTypeRate;
- u.rate = value;
- }
- }
-
- status_t writeToParcel(Parcel *data) const;
- status_t readFromParcel(const Parcel& data);
- void toString(char *buffer, size_t length) const;
- size_t getByteStringSize() const;
- status_t writeToByteString(char **bufferpptr, char *bufferptrmax) const;
- status_t readFromByteString(const char **bufferpptr, const char *bufferptrmax);
-
- // TODO: make private (and consider converting to std::variant)
- // private:
- char *mName = nullptr;
- Type mType = kTypeNone;
- union u__ {
- u__() { zero(); }
- u__(u__ &&other) {
- *this = std::move(other);
- }
- u__& operator=(u__ &&other) {
- memcpy(this, &other, sizeof(*this));
- other.zero();
- return *this;
- }
- void zero() { memset(this, 0, sizeof(*this)); }
-
- int32_t int32Value;
- int64_t int64Value;
- double doubleValue;
- char *CStringValue;
- std::pair<int64_t, int64_t> rate;
- } u;
- };
-
- class iterator {
- public:
- iterator(size_t pos, const MediaAnalyticsItem &_item)
- : i(std::min(pos, _item.count()))
- , item(_item) { }
- iterator &operator++() {
- i = std::min(i + 1, item.count());
- return *this;
- }
- bool operator!=(iterator &other) const {
- return i != other.i;
- }
- Prop &operator*() const {
- return item.mProps[i];
- }
-
- private:
- size_t i;
- const MediaAnalyticsItem &item;
- };
-
- iterator begin() const {
- return iterator(0, *this);
- }
- iterator end() const {
- return iterator(SIZE_MAX, *this);
- }
-
-private:
-
- // TODO: make prop management class
- size_t findPropIndex(const char *name) const;
- Prop *findProp(const char *name) const;
- Prop *allocateProp();
-
- enum {
- kGrowProps = 10
- };
- bool growProps(int increment = kGrowProps);
- Prop *allocateProp(const char *name);
- bool removeProp(const char *name);
- Prop *allocateProp(const std::string& name) { return allocateProp(name.c_str()); }
-
- size_t mPropCount = 0;
- size_t mPropSize = 0;
- Prop *mProps = nullptr;
-
- pid_t mPid = -1;
- uid_t mUid = -1;
- std::string mPkgName;
- int64_t mPkgVersionCode = 0;
- std::string mKey{kKeyNone};
- nsecs_t mTimestamp = 0;
-};
-
-} // namespace android
-
-#endif
diff --git a/media/libmediametrics/include/MediaMetricsConstants.h b/media/libmediametrics/include/MediaMetricsConstants.h
new file mode 100644
index 0000000..e49917f
--- /dev/null
+++ b/media/libmediametrics/include/MediaMetricsConstants.h
@@ -0,0 +1,135 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_MEDIA_MEDIAMETRICSCONSTANTS_H
+#define ANDROID_MEDIA_MEDIAMETRICSCONSTANTS_H
+
+/*
+ * MediaMetrics Keys and Properties.
+ *
+ * C/C++ friendly constants that ensure
+ * 1) Compilation error on misspelling
+ * 2) Consistent behavior and documentation.
+ */
+
+/*
+ * Taxonomy of audio keys
+ *
+ * To build longer keys, we use compiler string concatenation of
+ * adjacent string literals. This is done in the translation phase
+ * of compilation to make a single string token.
+ */
+
+// Key Prefixes are used for MediaMetrics Item Keys and ends with a ".".
+// They must be appended with another value to make a key.
+#define AMEDIAMETRICS_KEY_PREFIX_AUDIO "audio."
+
+// The AudioRecord key appends the "trackId" to the prefix.
+#define AMEDIAMETRICS_KEY_PREFIX_AUDIO_RECORD AMEDIAMETRICS_KEY_PREFIX_AUDIO "record."
+
+// The AudioThread key appends the "threadId" to the prefix.
+#define AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD AMEDIAMETRICS_KEY_PREFIX_AUDIO "thread."
+
+// The AudioTrack key appends the "trackId" to the prefix.
+#define AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK AMEDIAMETRICS_KEY_PREFIX_AUDIO "track."
+
+// Keys are strings used for MediaMetrics Item Keys
+#define AMEDIAMETRICS_KEY_AUDIO_FLINGER AMEDIAMETRICS_KEY_PREFIX_AUDIO "flinger"
+#define AMEDIAMETRICS_KEY_AUDIO_POLICY AMEDIAMETRICS_KEY_PREFIX_AUDIO "policy"
+
+/*
+ * MediaMetrics Properties are unified space for consistency and readability.
+ */
+
+// Property prefixes may be applied before a property name to indicate a specific
+// category to which it is associated.
+#define AMEDIAMETRICS_PROP_PREFIX_EFFECTIVE "effective."
+#define AMEDIAMETRICS_PROP_PREFIX_HAL "hal."
+#define AMEDIAMETRICS_PROP_PREFIX_HAPTIC "haptic."
+#define AMEDIAMETRICS_PROP_PREFIX_SERVER "server."
+
+// Properties within mediametrics are string constants denoted by
+// a macro name beginning with AMEDIAMETRICS_PROP_*
+//
+// For a property name like "auxEffectId" we write this as a single upper case word
+// at the end of the macro name, such as AMEDIAMETRICS_PROP_AUXEFFECTID.
+//
+// Underscores after the AMEDIAMETRICS_PROP_* prefix indicate
+// a "dot" in the property name. For example AMEDIAMETRICS_PROP_VOLUME_LEFT
+// corresponds to "volume.left".
+
+#define AMEDIAMETRICS_PROP_AUXEFFECTID "auxEffectId" // int32 (AudioTrack)
+#define AMEDIAMETRICS_PROP_CHANNELCOUNT "channelCount" // int32
+#define AMEDIAMETRICS_PROP_CHANNELMASK "channelMask" // int32
+#define AMEDIAMETRICS_PROP_CONTENTTYPE "contentType" // string attributes (AudioTrack)
+#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)
+
+// TODO: fix inconsistency in flags: AudioRecord / AudioTrack int32, AudioThread string
+#define AMEDIAMETRICS_PROP_FLAGS "flags"
+
+#define AMEDIAMETRICS_PROP_FRAMECOUNT "frameCount" // int32
+#define AMEDIAMETRICS_PROP_INPUTDEVICES "inputDevices" // string value
+#define AMEDIAMETRICS_PROP_LATENCYMS "latencyMs" // double value
+#define AMEDIAMETRICS_PROP_ORIGINALFLAGS "originalFlags" // int32
+#define AMEDIAMETRICS_PROP_OUTPUTDEVICES "outputDevices" // string value
+#define AMEDIAMETRICS_PROP_PLAYBACK_PITCH "playback.pitch" // double value (AudioTrack)
+#define AMEDIAMETRICS_PROP_PLAYBACK_SPEED "playback.speed" // double value (AudioTrack)
+#define AMEDIAMETRICS_PROP_ROUTEDDEVICEID "routedDeviceId" // int32
+#define AMEDIAMETRICS_PROP_SAMPLERATE "sampleRate" // int32
+#define AMEDIAMETRICS_PROP_SELECTEDDEVICEID "selectedDeviceId" // int32
+#define AMEDIAMETRICS_PROP_SELECTEDMICDIRECTION "selectedMicDirection" // int32
+#define AMEDIAMETRICS_PROP_SELECTEDMICFIELDDIRECTION "selectedMicFieldDimension" // double
+#define AMEDIAMETRICS_PROP_SESSIONID "sessionId" // int32
+#define AMEDIAMETRICS_PROP_SOURCE "source" // string (AudioAttributes)
+#define AMEDIAMETRICS_PROP_STARTUPMS "startupMs" // double value
+// State is "ACTIVE" or "STOPPED" for AudioRecord
+#define AMEDIAMETRICS_PROP_STATE "state" // string
+#define AMEDIAMETRICS_PROP_STATUS "status" // int32 status_t
+#define AMEDIAMETRICS_PROP_STREAMTYPE "streamType" // string (AudioTrack)
+#define AMEDIAMETRICS_PROP_THREADID "threadId" // int32 value io handle
+#define AMEDIAMETRICS_PROP_THROTTLEMS "throttleMs" // double
+#define AMEDIAMETRICS_PROP_TRACKID "trackId" // int32 port id of track/record
+#define AMEDIAMETRICS_PROP_TYPE "type" // string (thread type)
+#define AMEDIAMETRICS_PROP_UNDERRUN "underrun" // int32
+#define AMEDIAMETRICS_PROP_USAGE "usage" // string attributes (ATrack)
+#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
+
+// Timing values: millisecond values are suffixed with MS and the type is double
+// nanosecond values are suffixed with NS and the type is int64.
+
+// Values are strings accepted for a given property.
+
+// An event is a general description, which often is a function name.
+#define AMEDIAMETRICS_PROP_EVENT_VALUE_CREATE "create"
+#define AMEDIAMETRICS_PROP_EVENT_VALUE_CREATEAUDIOPATCH "createAudioPatch"
+#define AMEDIAMETRICS_PROP_EVENT_VALUE_CTOR "ctor"
+#define AMEDIAMETRICS_PROP_EVENT_VALUE_DTOR "dtor"
+#define AMEDIAMETRICS_PROP_EVENT_VALUE_FLUSH "flush" // AudioTrack
+#define AMEDIAMETRICS_PROP_EVENT_VALUE_INVALIDATE "invalidate" // server track, record
+#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_SETPLAYBACKPARAM "setPlaybackParam" // AudioTrack
+#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
+#define AMEDIAMETRICS_PROP_EVENT_VALUE_UNDERRUN "underrun" // from Thread
+
+#endif // ANDROID_MEDIA_MEDIAMETRICSCONSTANTS_H
diff --git a/media/libmediametrics/include/IMediaAnalyticsService.h b/media/libmediametrics/include/media/IMediaMetricsService.h
similarity index 83%
rename from media/libmediametrics/include/IMediaAnalyticsService.h
rename to media/libmediametrics/include/media/IMediaMetricsService.h
index 30c63e7..d6871ec 100644
--- a/media/libmediametrics/include/IMediaAnalyticsService.h
+++ b/media/libmediametrics/include/media/IMediaMetricsService.h
@@ -29,15 +29,14 @@
#include <binder/IServiceManager.h>
-#include <media/MediaAnalyticsItem.h>
-// nope...#include <media/MediaAnalytics.h>
+#include <media/MediaMetricsItem.h>
namespace android {
-class IMediaAnalyticsService: public IInterface
+class IMediaMetricsService: public IInterface
{
public:
- DECLARE_META_INTERFACE(MediaAnalyticsService);
+ DECLARE_META_INTERFACE(MediaMetricsService);
/**
* Submits the indicated record to the mediaanalytics service, where
@@ -48,14 +47,14 @@
* \return status which is negative if an error is detected (some errors
may be silent and return 0 - success).
*/
- virtual status_t submit(MediaAnalyticsItem *item) = 0;
+ virtual status_t submit(mediametrics::Item *item) = 0;
virtual status_t submitBuffer(const char *buffer, size_t length) = 0;
};
// ----------------------------------------------------------------------------
-class BnMediaAnalyticsService: public BnInterface<IMediaAnalyticsService>
+class BnMediaMetricsService: public BnInterface<IMediaMetricsService>
{
public:
status_t onTransact(uint32_t code,
@@ -66,7 +65,7 @@
protected:
// Internal call where release is true if the service is to delete the item.
virtual status_t submitInternal(
- MediaAnalyticsItem *item, bool release) = 0;
+ mediametrics::Item *item, bool release) = 0;
};
}; // namespace android
diff --git a/media/libmediametrics/include/MediaMetrics.h b/media/libmediametrics/include/media/MediaMetrics.h
similarity index 98%
rename from media/libmediametrics/include/MediaMetrics.h
rename to media/libmediametrics/include/media/MediaMetrics.h
index 29fb241..76abe86 100644
--- a/media/libmediametrics/include/MediaMetrics.h
+++ b/media/libmediametrics/include/media/MediaMetrics.h
@@ -24,6 +24,9 @@
// for that string to the caller. The caller is responsible for calling free()
// on that pointer when done using the value.
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
__BEGIN_DECLS
// internally re-cast to the behind-the-scenes C++ class instance
diff --git a/media/libmediametrics/include/media/MediaMetricsItem.h b/media/libmediametrics/include/media/MediaMetricsItem.h
new file mode 100644
index 0000000..08720f1
--- /dev/null
+++ b/media/libmediametrics/include/media/MediaMetricsItem.h
@@ -0,0 +1,1187 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_MEDIAMETRICSITEM_H
+#define ANDROID_MEDIA_MEDIAMETRICSITEM_H
+
+#include "MediaMetrics.h"
+#include "MediaMetricsConstants.h"
+
+#include <algorithm>
+#include <map>
+#include <string>
+#include <sys/types.h>
+#include <variant>
+
+#include <binder/Parcel.h>
+#include <cutils/properties.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/RefBase.h>
+#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
+
+namespace android {
+
+class IMediaMetricsService;
+class Parcel;
+
+/*
+ * MediaMetrics Item
+ *
+ * The MediaMetrics Item allows get/set operations and recording to the service.
+ *
+ * The MediaMetrics LogItem is a faster logging variant. It allows set operations only,
+ * and then recording to the service.
+ *
+ * The Byte String format is as follows:
+ *
+ * For Java
+ * int64 corresponds to long
+ * int32, uint32 corresponds to int
+ * uint16 corresponds to char
+ * uint8, int8 corresponds to byte
+ *
+ * For items transmitted from Java, uint8 and uint32 values are limited
+ * to INT8_MAX and INT32_MAX. This constrains the size of large items
+ * to 2GB, which is consistent with ByteBuffer max size. A native item
+ * can conceivably have size of 4GB.
+ *
+ * Physical layout of integers and doubles within the MediaMetrics byte string
+ * is in Native / host order, which is usually little endian.
+ *
+ * Note that primitive data (ints, doubles) within a Byte String has
+ * no extra padding or alignment requirements, like ByteBuffer.
+ *
+ * -- begin of item
+ * -- begin of header
+ * (uint32) item size: including the item size field
+ * (uint32) header size, including the item size and header size fields.
+ * (uint16) version: exactly 0
+ * (uint16) key size, that is key strlen + 1 for zero termination.
+ * (int8)+ key, a string which is 0 terminated (UTF-8).
+ * (int32) pid
+ * (int32) uid
+ * (int64) timestamp
+ * -- end of header
+ * -- begin body
+ * (uint32) number of properties
+ * -- repeat for number of properties
+ * (uint16) property size, including property size field itself
+ * (uint8) type of property
+ * (int8)+ key string, including 0 termination
+ * based on type of property (given above), one of:
+ * (int32)
+ * (int64)
+ * (double)
+ * (int8)+ for TYPE_CSTRING, including 0 termination
+ * (int64, int64) for rate
+ * -- end body
+ * -- end of item
+ *
+ * The Byte String format must match MediaMetrics.java.
+ */
+
+namespace mediametrics {
+
+// Type must match MediaMetrics.java
+enum Type {
+ kTypeNone = 0,
+ kTypeInt32 = 1,
+ kTypeInt64 = 2,
+ kTypeDouble = 3,
+ kTypeCString = 4,
+ kTypeRate = 5,
+};
+
+/*
+ * Time printing
+ *
+ * kPrintFormatLong time string is 19 characters (including null termination).
+ * Example Long Form: "03-27 16:47:06.187"
+ * MM DD HH MM SS MS
+ *
+ * kPrintFormatShort time string is 13 characters (including null termination).
+ * Example Short Form: "16:47:06.187"
+ * HH MM SS MS
+ */
+
+enum PrintFormat {
+ kPrintFormatLong = 0,
+ kPrintFormatShort = 1,
+};
+
+/**
+ * Converts real time in ns to a time string object, with format similar to logcat.
+ *
+ * \param ns input real time in nanoseconds to convert.
+ * \param buffer the buffer location to put the converted string.
+ * \param bufferSize the size of buffer in bytes.
+ * \param format format, from enum PrintFormat.
+ */
+void nsToString(
+ int64_t ns, char *buffer, size_t bufferSize, PrintFormat format = kPrintFormatLong);
+
+// Contains the time string
+struct time_string_t {
+ char time[19]; /* minimum size buffer */
+};
+
+/**
+ * Converts real time in ns to a time string object, with format similar to logcat.
+ *
+ * \param ns input real time in nanoseconds to convert.
+ * \param format format, from enum PrintFormat.
+ * \return a time_string_t object with the time string encoded.
+ */
+static inline time_string_t timeStringFromNs(int64_t ns, PrintFormat format = kPrintFormatLong) {
+ time_string_t ts;
+ nsToString(ns, ts.time, sizeof(ts.time), format);
+ return ts;
+}
+
+/**
+ * Finds the end of the common time prefix.
+ *
+ * This is as an option to remove the common time prefix to avoid
+ * unnecessary duplicated strings.
+ *
+ * \param time1 a time string from timeStringFromNs
+ * \param time2 a time string from timeStringFromNs
+ * \return the position where the common time prefix ends. For abbreviated
+ * printing of time2, offset the character pointer by this position.
+ */
+static inline size_t commonTimePrefixPosition(const char *time1, const char *time2) {
+ size_t i;
+
+ // Find location of the first mismatch between strings
+ for (i = 0; ; ++i) {
+ if (time1[i] != time2[i]) {
+ break;
+ }
+ if (time1[i] == 0) {
+ return i; // strings match completely
+ }
+ }
+
+ // Go backwards until we find a delimeter or space.
+ for (; i > 0
+ && isdigit(time1[i]) // still a number
+ && time1[i - 1] != ' '
+ ; --i) {
+ }
+ return i;
+}
+
+/**
+ * The MediaMetrics Item has special Item properties,
+ * derived internally or through dedicated setters.
+ *
+ * For consistency we use the following keys to represent
+ * these special Item properties when in a generic Bundle
+ * or in a std::map.
+ *
+ * These values must match MediaMetrics.java
+ */
+static inline constexpr const char *BUNDLE_TOTAL_SIZE = "_totalSize";
+static inline constexpr const char *BUNDLE_HEADER_SIZE = "_headerSize";
+static inline constexpr const char *BUNDLE_VERSION = "_version";
+static inline constexpr const char *BUNDLE_KEY_SIZE = "_keySize";
+static inline constexpr const char *BUNDLE_KEY = "_key";
+static inline constexpr const char *BUNDLE_PID = "_pid";
+static inline constexpr const char *BUNDLE_UID = "_uid";
+static inline constexpr const char *BUNDLE_TIMESTAMP = "_timestamp";
+static inline constexpr const char *BUNDLE_PROPERTY_COUNT = "_propertyCount";
+
+template<size_t N>
+static inline bool startsWith(const std::string &s, const char (&comp)[N]) {
+ return !strncmp(s.c_str(), comp, N - 1);
+}
+
+static inline bool startsWith(const std::string& s, const std::string& comp) {
+ return !strncmp(s.c_str(), comp.c_str(), comp.size() - 1);
+}
+
+/**
+ * Defers a function to run in the destructor.
+ *
+ * This helper class is used to log results on exit of a method.
+ */
+class Defer {
+public:
+ template <typename U>
+ Defer(U &&f) : mThunk(std::forward<U>(f)) {}
+ ~Defer() { mThunk(); }
+
+private:
+ const std::function<void()> mThunk;
+};
+
+/**
+ * Media Metrics BaseItem
+ *
+ * A base class which contains utility static functions to write to a byte stream
+ * and access the Media Metrics service.
+ */
+
+class BaseItem {
+ friend class MediaMetricsDeathNotifier; // for dropInstance
+ // enabled 1, disabled 0
+public:
+ // are we collecting metrics data
+ static bool isEnabled();
+ static sp<IMediaMetricsService> getService();
+
+protected:
+ static constexpr const char * const EnabledProperty = "media.metrics.enabled";
+ static constexpr const char * const EnabledPropertyPersist = "persist.media.metrics.enabled";
+ static const int EnabledProperty_default = 1;
+
+ // let's reuse a binder connection
+ static sp<IMediaMetricsService> sMediaMetricsService;
+
+ static void dropInstance();
+ static bool submitBuffer(const char *buffer, size_t len);
+
+ template <typename T>
+ struct is_item_type {
+ static constexpr inline bool value =
+ std::is_same<T, int32_t>::value
+ || std::is_same<T, int64_t>::value
+ || std::is_same<T, double>::value
+ || std::is_same<T, std::pair<int64_t, int64_t>>:: value
+ || std::is_same<T, std::string>::value
+ || std::is_same<T, std::monostate>::value;
+ };
+
+ template <typename T>
+ struct get_type_of {
+ static_assert(is_item_type<T>::value);
+ static constexpr inline Type value =
+ std::is_same<T, int32_t>::value ? kTypeInt32
+ : std::is_same<T, int64_t>::value ? kTypeInt64
+ : std::is_same<T, double>::value ? kTypeDouble
+ : std::is_same<T, std::pair<int64_t, int64_t>>:: value ? kTypeRate
+ : std::is_same<T, std::string>::value ? kTypeCString
+ : std::is_same<T, std::monostate>::value ? kTypeNone
+ : kTypeNone;
+ };
+
+ template <typename T>
+ static size_t sizeOfByteString(const char *name, const T& value) {
+ static_assert(is_item_type<T>::value);
+ return 2 + 1 + strlen(name) + 1 + sizeof(value);
+ }
+ template <> // static
+ size_t sizeOfByteString(const char *name, const std::string& value) {
+ return 2 + 1 + strlen(name) + 1 + value.size() + 1;
+ }
+ template <> // static
+ size_t sizeOfByteString(const char *name, const std::monostate&) {
+ return 2 + 1 + strlen(name) + 1;
+ }
+ // for speed
+ static size_t sizeOfByteString(const char *name, const char *value) {
+ return 2 + 1 + strlen(name) + 1 + strlen(value) + 1;
+ }
+
+ template <typename T>
+ static status_t insert(const T& val, char **bufferpptr, char *bufferptrmax) {
+ static_assert(std::is_trivially_constructible<T>::value);
+ const size_t size = sizeof(val);
+ if (*bufferpptr + size > bufferptrmax) {
+ ALOGE("%s: buffer exceeded with size %zu", __func__, size);
+ return BAD_VALUE;
+ }
+ memcpy(*bufferpptr, &val, size);
+ *bufferpptr += size;
+ return NO_ERROR;
+ }
+ template <> // static
+ status_t insert(const std::string& val, char **bufferpptr, char *bufferptrmax) {
+ const size_t size = val.size() + 1;
+ if (size > UINT16_MAX || *bufferpptr + size > bufferptrmax) {
+ ALOGE("%s: buffer exceeded with size %zu", __func__, size);
+ return BAD_VALUE;
+ }
+ memcpy(*bufferpptr, val.c_str(), size);
+ *bufferpptr += size;
+ return NO_ERROR;
+ }
+ template <> // static
+ status_t insert(const std::pair<int64_t, int64_t>& val,
+ char **bufferpptr, char *bufferptrmax) {
+ const size_t size = sizeof(val.first) + sizeof(val.second);
+ if (*bufferpptr + size > bufferptrmax) {
+ ALOGE("%s: buffer exceeded with size %zu", __func__, size);
+ return BAD_VALUE;
+ }
+ memcpy(*bufferpptr, &val.first, sizeof(val.first));
+ memcpy(*bufferpptr + sizeof(val.first), &val.second, sizeof(val.second));
+ *bufferpptr += size;
+ return NO_ERROR;
+ }
+ template <> // static
+ status_t insert(const std::monostate&, char **, char *) {
+ return NO_ERROR;
+ }
+ // for speed
+ static status_t insert(const char *val, char **bufferpptr, char *bufferptrmax) {
+ const size_t size = strlen(val) + 1;
+ if (size > UINT16_MAX || *bufferpptr + size > bufferptrmax) {
+ ALOGE("%s: buffer exceeded with size %zu", __func__, size);
+ return BAD_VALUE;
+ }
+ memcpy(*bufferpptr, val, size);
+ *bufferpptr += size;
+ return NO_ERROR;
+ }
+
+ template <typename T>
+ static status_t writeToByteString(
+ const char *name, const T& value, char **bufferpptr, char *bufferptrmax) {
+ static_assert(is_item_type<T>::value);
+ const size_t len = sizeOfByteString(name, value);
+ if (len > UINT16_MAX) return BAD_VALUE;
+ return insert((uint16_t)len, bufferpptr, bufferptrmax)
+ ?: insert((uint8_t)get_type_of<T>::value, bufferpptr, bufferptrmax)
+ ?: insert(name, bufferpptr, bufferptrmax)
+ ?: insert(value, bufferpptr, bufferptrmax);
+ }
+ // for speed
+ static status_t writeToByteString(
+ const char *name, const char *value, char **bufferpptr, char *bufferptrmax) {
+ const size_t len = sizeOfByteString(name, value);
+ if (len > UINT16_MAX) return BAD_VALUE;
+ return insert((uint16_t)len, bufferpptr, bufferptrmax)
+ ?: insert((uint8_t)kTypeCString, bufferpptr, bufferptrmax)
+ ?: insert(name, bufferpptr, bufferptrmax)
+ ?: insert(value, bufferpptr, bufferptrmax);
+ }
+
+ template <typename T>
+ static void toStringBuffer(
+ const char *name, const T& value, char *buffer, size_t length) = delete;
+ template <> // static
+ void toStringBuffer(
+ const char *name, const int32_t& value, char *buffer, size_t length) {
+ snprintf(buffer, length, "%s=%d", name, value);
+ }
+ template <> // static
+ void toStringBuffer(
+ const char *name, const int64_t& value, char *buffer, size_t length) {
+ snprintf(buffer, length, "%s=%lld", name, (long long)value);
+ }
+ template <> // static
+ void toStringBuffer(
+ const char *name, const double& value, char *buffer, size_t length) {
+ snprintf(buffer, length, "%s=%e", name, value);
+ }
+ template <> // static
+ void toStringBuffer(
+ const char *name, const std::pair<int64_t, int64_t>& value,
+ char *buffer, size_t length) {
+ snprintf(buffer, length, "%s=%lld/%lld",
+ name, (long long)value.first, (long long)value.second);
+ }
+ template <> // static
+ void toStringBuffer(
+ const char *name, const std::string& value, char *buffer, size_t length) {
+ // TODO sanitize string for ':' '='
+ snprintf(buffer, length, "%s=%s", name, value.c_str());
+ }
+ template <> // static
+ void toStringBuffer(
+ const char *name, const std::monostate&, char *buffer, size_t length) {
+ snprintf(buffer, length, "%s=()", name);
+ }
+
+ template <typename T>
+ static status_t writeToParcel(
+ const char *name, const T& value, Parcel *parcel) = delete;
+ template <> // static
+ status_t writeToParcel(
+ const char *name, const int32_t& value, Parcel *parcel) {
+ return parcel->writeCString(name)
+ ?: parcel->writeInt32(get_type_of<int32_t>::value)
+ ?: parcel->writeInt32(value);
+ }
+ template <> // static
+ status_t writeToParcel(
+ const char *name, const int64_t& value, Parcel *parcel) {
+ return parcel->writeCString(name)
+ ?: parcel->writeInt32(get_type_of<int64_t>::value)
+ ?: parcel->writeInt64(value);
+ }
+ template <> // static
+ status_t writeToParcel(
+ const char *name, const double& value, Parcel *parcel) {
+ return parcel->writeCString(name)
+ ?: parcel->writeInt32(get_type_of<double>::value)
+ ?: parcel->writeDouble(value);
+ }
+ template <> // static
+ status_t writeToParcel(
+ const char *name, const std::pair<int64_t, int64_t>& value, Parcel *parcel) {
+ return parcel->writeCString(name)
+ ?: parcel->writeInt32(get_type_of< std::pair<int64_t, int64_t>>::value)
+ ?: parcel->writeInt64(value.first)
+ ?: parcel->writeInt64(value.second);
+ }
+ template <> // static
+ status_t writeToParcel(
+ const char *name, const std::string& value, Parcel *parcel) {
+ return parcel->writeCString(name)
+ ?: parcel->writeInt32(get_type_of<std::string>::value)
+ ?: parcel->writeCString(value.c_str());
+ }
+ template <> // static
+ status_t writeToParcel(
+ const char *name, const std::monostate&, Parcel *parcel) {
+ return parcel->writeCString(name)
+ ?: parcel->writeInt32(get_type_of<std::monostate>::value);
+ }
+
+ template <typename T>
+ static status_t extract(T *val, const char **bufferpptr, const char *bufferptrmax) {
+ static_assert(std::is_trivially_constructible<T>::value);
+ const size_t size = sizeof(*val);
+ if (*bufferpptr + size > bufferptrmax) {
+ ALOGE("%s: buffer exceeded with size %zu", __func__, size);
+ return BAD_VALUE;
+ }
+ memcpy(val, *bufferpptr, size);
+ *bufferpptr += size;
+ return NO_ERROR;
+ }
+ template <> // static
+ status_t extract(std::string *val, const char **bufferpptr, const char *bufferptrmax) {
+ const char *ptr = *bufferpptr;
+ while (*ptr != 0) {
+ if (ptr >= bufferptrmax) {
+ ALOGE("%s: buffer exceeded", __func__);
+ return BAD_VALUE;
+ }
+ ++ptr;
+ }
+ const size_t size = (ptr - *bufferpptr) + 1;
+ *val = *bufferpptr;
+ *bufferpptr += size;
+ return NO_ERROR;
+ }
+ template <> // static
+ status_t extract(std::pair<int64_t, int64_t> *val,
+ const char **bufferpptr, const char *bufferptrmax) {
+ const size_t size = sizeof(val->first) + sizeof(val->second);
+ if (*bufferpptr + size > bufferptrmax) {
+ ALOGE("%s: buffer exceeded with size %zu", __func__, size);
+ return BAD_VALUE;
+ }
+ memcpy(&val->first, *bufferpptr, sizeof(val->first));
+ memcpy(&val->second, *bufferpptr + sizeof(val->first), sizeof(val->second));
+ *bufferpptr += size;
+ return NO_ERROR;
+ }
+ template <> // static
+ status_t extract(std::monostate *, const char **, const char *) {
+ return NO_ERROR;
+ }
+};
+
+/**
+ * Media Metrics BufferedItem
+ *
+ * A base class which represents a put-only Media Metrics item, storing
+ * the Media Metrics data in a buffer with begin and end pointers.
+ *
+ * If a property key is entered twice, it will be stored in the buffer twice,
+ * and (implementation defined) the last value for that key will be used
+ * by the Media Metrics service.
+ *
+ * For realloc, a baseRealloc pointer must be passed in either explicitly
+ * or implicitly in the constructor. This will be updated with the value used on realloc.
+ */
+class BufferedItem : public BaseItem {
+public:
+ static inline constexpr uint16_t kVersion = 0;
+
+ virtual ~BufferedItem() = default;
+ BufferedItem(const BufferedItem&) = delete;
+ BufferedItem& operator=(const BufferedItem&) = delete;
+
+ BufferedItem(const std::string key, char *begin, char *end)
+ : BufferedItem(key.c_str(), begin, end) { }
+
+ BufferedItem(const char *key, char *begin, char *end)
+ : BufferedItem(key, begin, end, nullptr) { }
+
+ BufferedItem(const char *key, char **begin, char *end)
+ : BufferedItem(key, *begin, end, begin) { }
+
+ BufferedItem(const char *key, char *begin, char *end, char **baseRealloc)
+ : mBegin(begin)
+ , mEnd(end)
+ , mBaseRealloc(baseRealloc)
+ {
+ init(key);
+ }
+
+ template<typename T>
+ BufferedItem &set(const char *key, const T& value) {
+ reallocFor(sizeOfByteString(key, value));
+ if (mStatus == NO_ERROR) {
+ mStatus = BaseItem::writeToByteString(key, value, &mBptr, mEnd);
+ ++mPropCount;
+ }
+ return *this;
+ }
+
+ template<typename T>
+ BufferedItem &set(const std::string& key, const T& value) {
+ return set(key.c_str(), value);
+ }
+
+ BufferedItem &setPid(pid_t pid) {
+ if (mStatus == NO_ERROR) {
+ copyTo(mBegin + mHeaderLen - 16, (int32_t)pid);
+ }
+ return *this;
+ }
+
+ BufferedItem &setUid(uid_t uid) {
+ if (mStatus == NO_ERROR) {
+ copyTo(mBegin + mHeaderLen - 12, (int32_t)uid);
+ }
+ return *this;
+ }
+
+ BufferedItem &setTimestamp(nsecs_t timestamp) {
+ if (mStatus == NO_ERROR) {
+ copyTo(mBegin + mHeaderLen - 8, (int64_t)timestamp);
+ }
+ return *this;
+ }
+
+ bool record() {
+ return updateHeader()
+ && BaseItem::submitBuffer(getBuffer(), getLength());
+ }
+
+ bool isValid () const {
+ return mStatus == NO_ERROR;
+ }
+
+ char *getBuffer() const { return mBegin; }
+ size_t getLength() const { return mBptr - mBegin; }
+ size_t getRemaining() const { return mEnd - mBptr; }
+ size_t getCapacity() const { return mEnd - mBegin; }
+
+ bool updateHeader() {
+ if (mStatus != NO_ERROR) return false;
+ copyTo(mBegin + 0, (uint32_t)getLength());
+ copyTo(mBegin + 4, (uint32_t)mHeaderLen);
+ copyTo(mBegin + mHeaderLen, (uint32_t)mPropCount);
+ return true;
+ }
+
+protected:
+ BufferedItem() = default;
+
+ void reallocFor(size_t required) {
+ if (mStatus != NO_ERROR) return;
+ const size_t remaining = getRemaining();
+ if (required <= remaining) return;
+ if (mBaseRealloc == nullptr) {
+ mStatus = NO_MEMORY;
+ return;
+ }
+
+ const size_t current = getLength();
+ size_t minimum = current + required;
+ if (minimum > SSIZE_MAX >> 1) {
+ mStatus = NO_MEMORY;
+ return;
+ }
+ minimum <<= 1;
+ void *newptr = realloc(*mBaseRealloc, minimum);
+ if (newptr == nullptr) {
+ mStatus = NO_MEMORY;
+ return;
+ }
+ if (newptr != *mBaseRealloc) {
+ // ALOGD("base changed! current:%zu new size %zu", current, minimum);
+ if (*mBaseRealloc == nullptr) {
+ memcpy(newptr, mBegin, current);
+ }
+ mBegin = (char *)newptr;
+ *mBaseRealloc = mBegin;
+ mEnd = mBegin + minimum;
+ mBptr = mBegin + current;
+ } else {
+ // ALOGD("base kept! current:%zu new size %zu", current, minimum);
+ mEnd = mBegin + minimum;
+ }
+ }
+ template<typename T>
+ void copyTo(char *ptr, const T& value) {
+ memcpy(ptr, &value, sizeof(value));
+ }
+
+ void init(const char *key) {
+ mBptr = mBegin;
+ const size_t keylen = key == nullptr ? 0 : strlen(key) + 1;
+ if (keylen <= 1) {
+ mStatus = BAD_VALUE; // prevent null pointer or empty keys.
+ return;
+ }
+ mHeaderLen = 4 + 4 + 2 + 2 + keylen + 4 + 4 + 8;
+ reallocFor(mHeaderLen);
+ if (mStatus != NO_ERROR) return;
+ mBptr = mBegin + mHeaderLen + 4; // this includes propcount.
+
+ if (mEnd < mBptr || keylen > UINT16_MAX) {
+ mStatus = NO_MEMORY;
+ mBptr = mEnd;
+ return;
+ }
+ copyTo(mBegin + 8, kVersion);
+ copyTo(mBegin + 10, (uint16_t)keylen);
+ strcpy(mBegin + 12, key);
+
+ // initialize some parameters (that could be overridden)
+ setPid(-1);
+ setUid(-1);
+ setTimestamp(0);
+ }
+
+ char *mBegin = nullptr;
+ char *mEnd = nullptr;
+ char **mBaseRealloc = nullptr; // set to an address if realloc should be done.
+ // upon return, that pointer is updated with
+ // whatever needs to be freed.
+ char *mBptr = nullptr;
+ status_t mStatus = NO_ERROR;
+ uint32_t mPropCount = 0;
+ uint32_t mHeaderLen = 0;
+};
+
+/**
+ * MediaMetrics LogItem is a stack allocated mediametrics item used for
+ * fast logging. It falls over to a malloc if needed.
+ *
+ * This is templated with a buffer size to allocate on the stack.
+ */
+template <size_t N = 4096>
+class LogItem : public BufferedItem {
+public:
+ explicit LogItem(const std::string key) : LogItem(key.c_str()) { }
+
+ // Since this class will not be defined before the base class, we initialize variables
+ // in our own order.
+ explicit LogItem(const char *key) {
+ mBegin = mBuffer;
+ mEnd = mBuffer + N;
+ mBaseRealloc = &mReallocPtr;
+ init(key);
+ }
+
+ ~LogItem() override {
+ if (mReallocPtr != nullptr) { // do the check before calling free to avoid overhead.
+ free(mReallocPtr);
+ }
+ }
+
+private:
+ char *mReallocPtr = nullptr; // set non-null by base class if realloc happened.
+ char mBuffer[N];
+};
+
+
+/**
+ * Media Metrics Item
+ *
+ * A mutable item representing an event or record that will be
+ * logged with the Media Metrics service. For client logging, one should
+ * use the mediametrics::Item.
+ *
+ * The Item is designed for the service as it has getters.
+ */
+class Item final : public mediametrics::BaseItem {
+public:
+
+ class Prop {
+ public:
+ using Elem = std::variant<
+ std::monostate, // kTypeNone
+ int32_t, // kTypeInt32
+ int64_t, // kTypeInt64
+ double, // kTypeDouble
+ std::string, // kTypeCString
+ std::pair<int64_t, int64_t> // kTypeRate
+ >;
+
+ Prop() = default;
+ Prop(const Prop& other) {
+ *this = other;
+ }
+ Prop& operator=(const Prop& other) {
+ mName = other.mName;
+ mElem = other.mElem;
+ return *this;
+ }
+ Prop(Prop&& other) {
+ *this = std::move(other);
+ }
+ Prop& operator=(Prop&& other) {
+ mName = std::move(other.mName);
+ mElem = std::move(other.mElem);
+ return *this;
+ }
+
+ bool operator==(const Prop& other) const {
+ return mName == other.mName && mElem == other.mElem;
+ }
+ bool operator!=(const Prop& other) const {
+ return !(*this == other);
+ }
+
+ void clear() {
+ mName.clear();
+ mElem = std::monostate{};
+ }
+ void clearValue() {
+ mElem = std::monostate{};
+ }
+
+ const char *getName() const {
+ return mName.c_str();
+ }
+
+ void swap(Prop& other) {
+ std::swap(mName, other.mName);
+ std::swap(mElem, other.mElem);
+ }
+
+ void setName(const char *name) {
+ mName = name;
+ }
+
+ bool isNamed(const char *name) const {
+ return mName == name;
+ }
+
+ template <typename T> void visit(T f) const {
+ std::visit(f, mElem);
+ }
+
+ template <typename T> bool get(T *value) const {
+ auto pval = std::get_if<T>(&mElem);
+ if (pval != nullptr) {
+ *value = *pval;
+ return true;
+ }
+ return false;
+ }
+
+ const Elem& get() const {
+ return mElem;
+ }
+
+ template <typename T> void set(const T& value) {
+ mElem = value;
+ }
+
+ template <typename T> void add(const T& value) {
+ auto pval = std::get_if<T>(&mElem);
+ if (pval != nullptr) {
+ *pval += value;
+ } else {
+ mElem = value;
+ }
+ }
+
+ template <> void add(const std::pair<int64_t, int64_t>& value) {
+ auto pval = std::get_if<std::pair<int64_t, int64_t>>(&mElem);
+ if (pval != nullptr) {
+ pval->first += value.first;
+ pval->second += value.second;
+ } else {
+ mElem = value;
+ }
+ }
+
+ status_t writeToParcel(Parcel *parcel) const {
+ return std::visit([this, parcel](auto &value) {
+ return BaseItem::writeToParcel(mName.c_str(), value, parcel);}, mElem);
+ }
+
+ void toStringBuffer(char *buffer, size_t length) const {
+ return std::visit([this, buffer, length](auto &value) {
+ BaseItem::toStringBuffer(mName.c_str(), value, buffer, length);}, mElem);
+ }
+
+ size_t getByteStringSize() const {
+ return std::visit([this](auto &value) {
+ return BaseItem::sizeOfByteString(mName.c_str(), value);}, mElem);
+ }
+
+ status_t writeToByteString(char **bufferpptr, char *bufferptrmax) const {
+ return std::visit([this, bufferpptr, bufferptrmax](auto &value) {
+ return BaseItem::writeToByteString(mName.c_str(), value, bufferpptr, bufferptrmax);
+ }, mElem);
+ }
+
+ status_t readFromParcel(const Parcel& data);
+
+ status_t readFromByteString(const char **bufferpptr, const char *bufferptrmax);
+
+ private:
+ std::string mName;
+ Elem mElem;
+ };
+
+ // Iteration of props within item
+ class iterator {
+ public:
+ iterator(const std::map<std::string, Prop>::const_iterator &_it) : it(_it) { }
+ iterator &operator++() {
+ ++it;
+ return *this;
+ }
+ bool operator!=(iterator &other) const {
+ return it != other.it;
+ }
+ const Prop &operator*() const {
+ return it->second;
+ }
+
+ private:
+ std::map<std::string, Prop>::const_iterator it;
+ };
+
+ iterator begin() const {
+ return iterator(mProps.cbegin());
+ }
+
+ iterator end() const {
+ return iterator(mProps.cend());
+ }
+
+ // T must be convertible to mKey
+ template <typename T>
+ explicit Item(T key)
+ : mKey(key) { }
+ Item() = default;
+
+ // We enable default copy and move constructors and make this class final
+ // to prevent a derived class; this avoids possible data slicing.
+ Item(const Item& other) = default;
+ Item(Item&& other) = default;
+ Item& operator=(const Item& other) = default;
+ Item& operator=(Item&& other) = default;
+
+ bool operator==(const Item& other) const {
+ return mPid == other.mPid
+ && mUid == other.mUid
+ && mPkgName == other.mPkgName
+ && mPkgVersionCode == other.mPkgVersionCode
+ && mKey == other.mKey
+ && mTimestamp == other.mTimestamp
+ && mProps == other.mProps
+ ;
+ }
+ bool operator!=(const Item& other) const {
+ return !(*this == other);
+ }
+
+ template <typename T>
+ static Item* create(T key) {
+ return new Item(key);
+ }
+ static Item* create() {
+ return new Item();
+ }
+
+ static Item* convert(mediametrics_handle_t);
+ static mediametrics_handle_t convert(Item *);
+
+ // access functions for the class
+ ~Item();
+
+ void clear() {
+ mPid = -1;
+ mUid = -1;
+ mPkgName.clear();
+ mPkgVersionCode = 0;
+ mTimestamp = 0;
+ mKey.clear();
+ mProps.clear();
+ }
+
+ Item *dup() const { return new Item(*this); }
+
+ Item &setKey(const char *key) {
+ mKey = key;
+ return *this;
+ }
+ const std::string& getKey() const { return mKey; }
+
+ // # of properties in the record
+ size_t count() const { return mProps.size(); }
+
+ template<typename S, typename T>
+ Item &set(S key, T value) {
+ findOrAllocateProp(key).set(value);
+ return *this;
+ }
+
+ // set values appropriately
+ Item &setInt32(const char *key, int32_t value) {
+ return set(key, value);
+ }
+ Item &setInt64(const char *key, int64_t value) {
+ return set(key, value);
+ }
+ Item &setDouble(const char *key, double value) {
+ return set(key, value);
+ }
+ Item &setRate(const char *key, int64_t count, int64_t duration) {
+ return set(key, std::make_pair(count, duration));
+ }
+ Item &setCString(const char *key, const char *value) {
+ return set(key, value);
+ }
+
+ // fused get/add/set; if attr wasn't there, it's a simple set.
+ // type-mismatch counts as "wasn't there".
+ template<typename S, typename T>
+ Item &add(S key, T value) {
+ findOrAllocateProp(key).add(value);
+ return *this;
+ }
+
+ Item &addInt32(const char *key, int32_t value) {
+ return add(key, value);
+ }
+ Item &addInt64(const char *key, int64_t value) {
+ return add(key, value);
+ }
+ Item &addDouble(const char *key, double value) {
+ return add(key, value);
+ }
+ Item &addRate(const char *key, int64_t count, int64_t duration) {
+ return add(key, std::make_pair(count, duration));
+ }
+
+ // find & extract values
+ // return indicates whether attr exists (and thus value filled in)
+ // NULL parameter value suppresses storage of value.
+ template<typename S, typename T>
+ bool get(S key, T *value) const {
+ const Prop *prop = findProp(key);
+ return prop != nullptr && prop->get(value);
+ }
+
+ bool getInt32(const char *key, int32_t *value) const {
+ return get(key, value);
+ }
+ bool getInt64(const char *key, int64_t *value) const {
+ return get(key, value);
+ }
+ bool getDouble(const char *key, double *value) const {
+ return get(key, value);
+ }
+ bool getRate(const char *key, int64_t *count, int64_t *duration, double *rate) const {
+ std::pair<int64_t, int64_t> value;
+ if (!get(key, &value)) return false;
+ if (count != nullptr) *count = value.first;
+ if (duration != nullptr) *duration = value.second;
+ if (rate != nullptr) {
+ if (value.second != 0) {
+ *rate = (double)value.first / value.second; // TODO: isn't INF OK?
+ } else {
+ *rate = 0.;
+ }
+ }
+ return true;
+ }
+ // Caller owns the returned string
+ bool getCString(const char *key, char **value) const {
+ std::string s;
+ if (get(key, &s)) {
+ *value = strdup(s.c_str());
+ return true;
+ }
+ return false;
+ }
+ bool getString(const char *key, std::string *value) const {
+ return get(key, value);
+ }
+
+ const Prop::Elem* get(const char *key) const {
+ const Prop *prop = findProp(key);
+ return prop == nullptr ? nullptr : &prop->get();
+ }
+
+ // Deliver the item to MediaMetrics
+ bool selfrecord();
+
+ // remove indicated attributes and their values
+ // filterNot() could also be called keepOnly()
+ // return value is # attributes removed
+ // XXX: perhaps 'remove' instead of 'filter'
+ // XXX: filterNot would become 'keep'
+ size_t filter(size_t count, const char *attrs[]);
+ size_t filterNot(size_t count, const char *attrs[]);
+ size_t filter(const char *attr) { return filter(1, &attr); }
+
+ // below here are used on server side or to talk to server
+ // clients need not worry about these.
+
+ // timestamp, pid, and uid only used on server side
+ // timestamp is in 'nanoseconds, unix time'
+ Item &setTimestamp(nsecs_t);
+ nsecs_t getTimestamp() const;
+
+ Item &setPid(pid_t);
+ pid_t getPid() const;
+
+ Item &setUid(uid_t);
+ uid_t getUid() const;
+
+ Item &setPkgName(const std::string &pkgName);
+ std::string getPkgName() const { return mPkgName; }
+
+ Item &setPkgVersionCode(int64_t);
+ int64_t getPkgVersionCode() const;
+
+ // our serialization code for binder calls
+ status_t writeToParcel(Parcel *) const;
+ status_t readFromParcel(const Parcel&);
+
+ status_t writeToByteString(char **bufferptr, size_t *length) const;
+ status_t readFromByteString(const char *bufferptr, size_t length);
+
+
+ std::string toString() const;
+ const char *toCString();
+
+ /**
+ * Returns true if the item has a property with a target value.
+ *
+ * If propName is nullptr, hasPropElem() returns false.
+ *
+ * \param propName is the property name.
+ * \param elem is the value to match. std::monostate matches any.
+ */
+ bool hasPropElem(const char *propName, const Prop::Elem& elem) const {
+ if (propName == nullptr) return false;
+ const Prop::Elem *e = get(propName);
+ return e != nullptr && (std::holds_alternative<std::monostate>(elem) || elem == *e);
+ }
+
+ /**
+ * Returns -2, -1, 0 (success) if the item has a property (wildcard matched) with a
+ * target value.
+ *
+ * The enum RecursiveWildcardCheck designates the meaning of the returned value.
+ *
+ * RECURSIVE_WILDCARD_CHECK_NO_MATCH_NO_WILDCARD = -2,
+ * RECURSIVE_WILDCARD_CHECK_NO_MATCH_WILDCARD_FOUND = -1,
+ * RECURSIVE_WILDCARD_CHECK_MATCH_FOUND = 0.
+ *
+ * If url is nullptr, RECURSIVE_WILDCARD_CHECK_NO_MATCH_NO_WILDCARD is returned.
+ *
+ * \param url is the full item + property name, which may have wildcards '*'
+ * denoting an arbitrary sequence of 0 or more characters.
+ * \param elem is the target property value to match. std::monostate matches any.
+ * \return 0 if the property was matched,
+ * -1 if the property was not matched and a wildcard char was encountered,
+ * -2 if the property was not matched with no wildcard char encountered.
+ */
+ enum RecursiveWildcardCheck {
+ RECURSIVE_WILDCARD_CHECK_NO_MATCH_NO_WILDCARD = -2,
+ RECURSIVE_WILDCARD_CHECK_NO_MATCH_WILDCARD_FOUND = -1,
+ RECURSIVE_WILDCARD_CHECK_MATCH_FOUND = 0,
+ };
+
+ enum RecursiveWildcardCheck recursiveWildcardCheckElem(
+ const char *url, const Prop::Elem& elem) const {
+ if (url == nullptr) return RECURSIVE_WILDCARD_CHECK_NO_MATCH_NO_WILDCARD;
+ return recursiveWildcardCheckElem(getKey().c_str(), url, elem);
+ }
+
+private:
+
+ enum RecursiveWildcardCheck recursiveWildcardCheckElem(
+ const char *itemKeyPtr, const char *url, const Prop::Elem& elem) const {
+ for (; *url && *itemKeyPtr; ++url, ++itemKeyPtr) {
+ if (*url != *itemKeyPtr) {
+ if (*url == '*') { // wildcard
+ ++url;
+ while (true) {
+ if (recursiveWildcardCheckElem(itemKeyPtr, url, elem)
+ == RECURSIVE_WILDCARD_CHECK_MATCH_FOUND) {
+ return RECURSIVE_WILDCARD_CHECK_MATCH_FOUND;
+ }
+ if (*itemKeyPtr == 0) break;
+ ++itemKeyPtr;
+ }
+ return RECURSIVE_WILDCARD_CHECK_NO_MATCH_WILDCARD_FOUND;
+ }
+ return RECURSIVE_WILDCARD_CHECK_NO_MATCH_NO_WILDCARD;
+ }
+ }
+ if (itemKeyPtr[0] != 0 || url[0] != '.') {
+ return RECURSIVE_WILDCARD_CHECK_NO_MATCH_NO_WILDCARD;
+ }
+ const char *propName = url + 1; // skip the '.'
+ return hasPropElem(propName, elem)
+ ? RECURSIVE_WILDCARD_CHECK_MATCH_FOUND
+ : RECURSIVE_WILDCARD_CHECK_NO_MATCH_NO_WILDCARD;
+ }
+
+ // handle Parcel version 0
+ int32_t writeToParcel0(Parcel *) const;
+ int32_t readFromParcel0(const Parcel&);
+
+ const Prop *findProp(const char *key) const {
+ auto it = mProps.find(key);
+ return it != mProps.end() ? &it->second : nullptr;
+ }
+
+ Prop &findOrAllocateProp(const char *key) {
+ auto it = mProps.find(key);
+ if (it != mProps.end()) return it->second;
+ Prop &prop = mProps[key];
+ prop.setName(key);
+ return prop;
+ }
+
+ // Changes to member variables below require changes to clear().
+ pid_t mPid = -1;
+ uid_t mUid = -1;
+ std::string mPkgName;
+ int64_t mPkgVersionCode = 0;
+ std::string mKey;
+ nsecs_t mTimestamp = 0;
+ std::map<std::string, Prop> mProps;
+};
+
+} // namespace mediametrics
+} // namespace android
+
+#endif // ANDROID_MEDIA_MEDIAMETRICSITEM_H
diff --git a/media/libmediaplayerservice/Android.bp b/media/libmediaplayerservice/Android.bp
index 762ed19..5301f5c 100644
--- a/media/libmediaplayerservice/Android.bp
+++ b/media/libmediaplayerservice/Android.bp
@@ -39,7 +39,6 @@
"libpowermanager",
"libstagefright",
"libstagefright_foundation",
- "libstagefright_framecapture_utils",
"libstagefright_httplive",
"libutils",
],
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index f2a38dd..81ffcbc 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -58,7 +58,6 @@
#include <media/Metadata.h>
#include <media/AudioTrack.h>
#include <media/MemoryLeakTrackUtil.h>
-#include <media/stagefright/FrameCaptureProcessor.h>
#include <media/stagefright/InterfaceUtils.h>
#include <media/stagefright/MediaCodecConstants.h>
#include <media/stagefright/MediaCodecList.h>
@@ -448,8 +447,6 @@
mNextConnId = 1;
MediaPlayerFactory::registerBuiltinFactories();
- // initialize the frame capture utilities
- (void)FrameCaptureProcessor::getInstance();
}
MediaPlayerService::~MediaPlayerService()
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index d36c6e3..c10e6e0 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -31,7 +31,7 @@
#include <binder/IServiceManager.h>
#include <media/IMediaPlayerService.h>
-#include <media/MediaAnalyticsItem.h>
+#include <media/MediaMetricsItem.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
@@ -138,7 +138,7 @@
}
// log the current record, provided it has some information worth recording
- // NB: this also reclaims & clears mAnalyticsItem.
+ // NB: this also reclaims & clears mMetricsItem.
flushAndResetMetrics(false);
}
@@ -147,69 +147,69 @@
// we run as part of the media player service; what we really want to
// know is the app which requested the recording.
- mAnalyticsItem->setUid(mClientUid);
+ mMetricsItem->setUid(mClientUid);
// populate the values from the raw fields.
// TBD mOutputFormat = OUTPUT_FORMAT_THREE_GPP;
// TBD mAudioEncoder = AUDIO_ENCODER_AMR_NB;
// TBD mVideoEncoder = VIDEO_ENCODER_DEFAULT;
- mAnalyticsItem->setInt32(kRecorderHeight, mVideoHeight);
- mAnalyticsItem->setInt32(kRecorderWidth, mVideoWidth);
- mAnalyticsItem->setInt32(kRecorderFrameRate, mFrameRate);
- mAnalyticsItem->setInt32(kRecorderVideoBitrate, mVideoBitRate);
- mAnalyticsItem->setInt32(kRecorderAudioSampleRate, mSampleRate);
- mAnalyticsItem->setInt32(kRecorderAudioChannels, mAudioChannels);
- mAnalyticsItem->setInt32(kRecorderAudioBitrate, mAudioBitRate);
+ mMetricsItem->setInt32(kRecorderHeight, mVideoHeight);
+ mMetricsItem->setInt32(kRecorderWidth, mVideoWidth);
+ mMetricsItem->setInt32(kRecorderFrameRate, mFrameRate);
+ mMetricsItem->setInt32(kRecorderVideoBitrate, mVideoBitRate);
+ mMetricsItem->setInt32(kRecorderAudioSampleRate, mSampleRate);
+ mMetricsItem->setInt32(kRecorderAudioChannels, mAudioChannels);
+ mMetricsItem->setInt32(kRecorderAudioBitrate, mAudioBitRate);
// TBD mInterleaveDurationUs = 0;
- mAnalyticsItem->setInt32(kRecorderVideoIframeInterval, mIFramesIntervalSec);
+ mMetricsItem->setInt32(kRecorderVideoIframeInterval, mIFramesIntervalSec);
// TBD mAudioSourceNode = 0;
// TBD mUse64BitFileOffset = false;
if (mMovieTimeScale != -1)
- mAnalyticsItem->setInt32(kRecorderMovieTimescale, mMovieTimeScale);
+ mMetricsItem->setInt32(kRecorderMovieTimescale, mMovieTimeScale);
if (mAudioTimeScale != -1)
- mAnalyticsItem->setInt32(kRecorderAudioTimescale, mAudioTimeScale);
+ mMetricsItem->setInt32(kRecorderAudioTimescale, mAudioTimeScale);
if (mVideoTimeScale != -1)
- mAnalyticsItem->setInt32(kRecorderVideoTimescale, mVideoTimeScale);
+ mMetricsItem->setInt32(kRecorderVideoTimescale, mVideoTimeScale);
// TBD mCameraId = 0;
// TBD mStartTimeOffsetMs = -1;
- mAnalyticsItem->setInt32(kRecorderVideoProfile, mVideoEncoderProfile);
- mAnalyticsItem->setInt32(kRecorderVideoLevel, mVideoEncoderLevel);
+ mMetricsItem->setInt32(kRecorderVideoProfile, mVideoEncoderProfile);
+ mMetricsItem->setInt32(kRecorderVideoLevel, mVideoEncoderLevel);
// TBD mMaxFileDurationUs = 0;
// TBD mMaxFileSizeBytes = 0;
// TBD mTrackEveryTimeDurationUs = 0;
- mAnalyticsItem->setInt32(kRecorderCaptureFpsEnable, mCaptureFpsEnable);
- mAnalyticsItem->setDouble(kRecorderCaptureFps, mCaptureFps);
+ mMetricsItem->setInt32(kRecorderCaptureFpsEnable, mCaptureFpsEnable);
+ mMetricsItem->setDouble(kRecorderCaptureFps, mCaptureFps);
// TBD mCameraSourceTimeLapse = NULL;
// TBD mMetaDataStoredInVideoBuffers = kMetadataBufferTypeInvalid;
// TBD mEncoderProfiles = MediaProfiles::getInstance();
- mAnalyticsItem->setInt32(kRecorderRotation, mRotationDegrees);
+ mMetricsItem->setInt32(kRecorderRotation, mRotationDegrees);
// PII mLatitudex10000 = -3600000;
// PII mLongitudex10000 = -3600000;
// TBD mTotalBitRate = 0;
// duration information (recorded, paused, # of pauses)
- mAnalyticsItem->setInt64(kRecorderDurationMs, (mDurationRecordedUs+500)/1000 );
+ mMetricsItem->setInt64(kRecorderDurationMs, (mDurationRecordedUs+500)/1000 );
if (mNPauses != 0) {
- mAnalyticsItem->setInt64(kRecorderPaused, (mDurationPausedUs+500)/1000 );
- mAnalyticsItem->setInt32(kRecorderNumPauses, mNPauses);
+ mMetricsItem->setInt64(kRecorderPaused, (mDurationPausedUs+500)/1000 );
+ mMetricsItem->setInt32(kRecorderNumPauses, mNPauses);
}
}
void StagefrightRecorder::flushAndResetMetrics(bool reinitialize) {
ALOGV("flushAndResetMetrics");
// flush anything we have, maybe setup a new record
- if (mAnalyticsDirty && mAnalyticsItem != NULL) {
+ if (mAnalyticsDirty && mMetricsItem != NULL) {
updateMetrics();
- if (mAnalyticsItem->count() > 0) {
- mAnalyticsItem->selfrecord();
+ if (mMetricsItem->count() > 0) {
+ mMetricsItem->selfrecord();
}
- delete mAnalyticsItem;
- mAnalyticsItem = NULL;
+ delete mMetricsItem;
+ mMetricsItem = NULL;
}
mAnalyticsDirty = false;
if (reinitialize) {
- mAnalyticsItem = MediaAnalyticsItem::create(kKeyRecorder);
+ mMetricsItem = mediametrics::Item::create(kKeyRecorder);
}
}
@@ -1184,10 +1184,10 @@
}
// log audio mime type for media metrics
- if (mAnalyticsItem != NULL) {
+ if (mMetricsItem != NULL) {
AString audiomime;
if (format->findString("mime", &audiomime)) {
- mAnalyticsItem->setCString(kRecorderAudioMime, audiomime.c_str());
+ mMetricsItem->setCString(kRecorderAudioMime, audiomime.c_str());
}
}
@@ -1745,10 +1745,10 @@
}
// log video mime type for media metrics
- if (mAnalyticsItem != NULL) {
+ if (mMetricsItem != NULL) {
AString videomime;
if (format->findString("mime", &videomime)) {
- mAnalyticsItem->setCString(kRecorderVideoMime, videomime.c_str());
+ mMetricsItem->setCString(kRecorderVideoMime, videomime.c_str());
}
}
@@ -2002,7 +2002,6 @@
(*meta)->setInt32(kKeyTimeScale, mMovieTimeScale);
}
if (mOutputFormat != OUTPUT_FORMAT_WEBM) {
- (*meta)->setInt32(kKey64BitFileOffset, mUse64BitFileOffset);
if (mTrackEveryTimeDurationUs > 0) {
(*meta)->setInt64(kKeyTrackTimeStatus, mTrackEveryTimeDurationUs);
}
@@ -2266,12 +2265,12 @@
return BAD_VALUE;
}
- if (mAnalyticsItem == NULL) {
+ if (mMetricsItem == NULL) {
return UNKNOWN_ERROR;
}
updateMetrics();
- mAnalyticsItem->writeToParcel(reply);
+ mMetricsItem->writeToParcel(reply);
return OK;
}
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index 9e0f4b7..a725bee 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -18,7 +18,7 @@
#define STAGEFRIGHT_RECORDER_H_
-#include <media/MediaAnalyticsItem.h>
+#include <media/MediaMetricsItem.h>
#include <media/MediaRecorderBase.h>
#include <camera/CameraParameters.h>
#include <utils/String8.h>
@@ -104,7 +104,7 @@
int mOutputFd;
sp<AudioSource> mAudioSourceNode;
- MediaAnalyticsItem *mAnalyticsItem;
+ mediametrics::Item *mMetricsItem;
bool mAnalyticsDirty;
void flushAndResetMetrics(bool reinitialize);
void updateMetrics();
diff --git a/media/libmediaplayerservice/nuplayer/Android.bp b/media/libmediaplayerservice/nuplayer/Android.bp
index c8f48a2..32c97cf 100644
--- a/media/libmediaplayerservice/nuplayer/Android.bp
+++ b/media/libmediaplayerservice/nuplayer/Android.bp
@@ -19,6 +19,7 @@
header_libs: [
"libmediadrm_headers",
+ "libmediametrics_headers",
"media_plugin_headers",
],
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index a49e72e..0eaa503 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -29,7 +29,7 @@
#include <datasource/NuCachedSource2.h>
#include <media/DataSource.h>
#include <media/MediaBufferHolder.h>
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <android/IMediaExtractorService.h>
#include <media/IMediaHTTPService.h>
#include <media/stagefright/foundation/ABuffer.h>
@@ -418,7 +418,7 @@
sp<IMediaExtractorService> mediaExService(
interface_cast<IMediaExtractorService>(binder));
sp<IDataSource> source;
- mediaExService->makeIDataSource(mFd, mOffset, mLength, &source);
+ mediaExService->makeIDataSource(base::unique_fd(dup(mFd.get())), mOffset, mLength, &source);
ALOGV("IDataSource(FileSource): %p %d %lld %lld",
source.get(), mFd.get(), (long long)mOffset, (long long)mLength);
if (source.get() != nullptr) {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 3388097..c1c4b55 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -1798,7 +1798,9 @@
}
void NuPlayer::closeAudioSink() {
- mRenderer->closeAudioSink();
+ if (mRenderer != NULL) {
+ mRenderer->closeAudioSink();
+ }
}
void NuPlayer::restartAudio(
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 4d9872a..24afd43 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -35,8 +35,6 @@
#include <media/stagefright/Utils.h>
#include <media/stagefright/FoundationUtils.h>
-#include <media/IMediaAnalyticsService.h>
-
static const int kDumpLockRetries = 50;
static const int kDumpLockSleepUs = 20000;
@@ -87,7 +85,7 @@
mMediaClock(new MediaClock),
mPlayer(new NuPlayer(pid, mMediaClock)),
mPlayerFlags(0),
- mAnalyticsItem(NULL),
+ mMetricsItem(NULL),
mClientUid(-1),
mAtEOS(false),
mLooping(false),
@@ -98,7 +96,7 @@
mMediaClock->init();
// set up an analytics record
- mAnalyticsItem = MediaAnalyticsItem::create(kKeyPlayer);
+ mMetricsItem = mediametrics::Item::create(kKeyPlayer);
mLooper->start(
false, /* runOnCallingThread */
@@ -118,9 +116,9 @@
updateMetrics("destructor");
logMetrics("destructor");
- if (mAnalyticsItem != NULL) {
- delete mAnalyticsItem;
- mAnalyticsItem = NULL;
+ if (mMetricsItem != NULL) {
+ delete mMetricsItem;
+ mMetricsItem = NULL;
}
}
@@ -131,8 +129,8 @@
status_t NuPlayerDriver::setUID(uid_t uid) {
mPlayer->setUID(uid);
mClientUid = uid;
- if (mAnalyticsItem) {
- mAnalyticsItem->setUid(mClientUid);
+ if (mMetricsItem) {
+ mMetricsItem->setUid(mClientUid);
}
return OK;
@@ -560,15 +558,15 @@
if (mime.startsWith("video/")) {
int32_t width, height;
- mAnalyticsItem->setCString(kPlayerVMime, mime.c_str());
+ mMetricsItem->setCString(kPlayerVMime, mime.c_str());
if (!name.empty()) {
- mAnalyticsItem->setCString(kPlayerVCodec, name.c_str());
+ mMetricsItem->setCString(kPlayerVCodec, name.c_str());
}
if (stats->findInt32("width", &width)
&& stats->findInt32("height", &height)) {
- mAnalyticsItem->setInt32(kPlayerWidth, width);
- mAnalyticsItem->setInt32(kPlayerHeight, height);
+ mMetricsItem->setInt32(kPlayerWidth, width);
+ mMetricsItem->setInt32(kPlayerHeight, height);
}
int64_t numFramesTotal = 0;
@@ -576,18 +574,18 @@
stats->findInt64("frames-total", &numFramesTotal);
stats->findInt64("frames-dropped-output", &numFramesDropped);
- mAnalyticsItem->setInt64(kPlayerFrames, numFramesTotal);
- mAnalyticsItem->setInt64(kPlayerFramesDropped, numFramesDropped);
+ mMetricsItem->setInt64(kPlayerFrames, numFramesTotal);
+ mMetricsItem->setInt64(kPlayerFramesDropped, numFramesDropped);
float frameRate = 0;
if (stats->findFloat("frame-rate-total", &frameRate)) {
- mAnalyticsItem->setDouble(kPlayerFrameRate, (double) frameRate);
+ mMetricsItem->setDouble(kPlayerFrameRate, (double) frameRate);
}
} else if (mime.startsWith("audio/")) {
- mAnalyticsItem->setCString(kPlayerAMime, mime.c_str());
+ mMetricsItem->setCString(kPlayerAMime, mime.c_str());
if (!name.empty()) {
- mAnalyticsItem->setCString(kPlayerACodec, name.c_str());
+ mMetricsItem->setCString(kPlayerACodec, name.c_str());
}
}
}
@@ -598,20 +596,20 @@
// getDuration() uses mLock for mutex -- careful where we use it.
int duration_ms = -1;
getDuration(&duration_ms);
- mAnalyticsItem->setInt64(kPlayerDuration, duration_ms);
+ mMetricsItem->setInt64(kPlayerDuration, duration_ms);
mPlayer->updateInternalTimers();
- mAnalyticsItem->setInt64(kPlayerPlaying, (mPlayingTimeUs+500)/1000 );
+ mMetricsItem->setInt64(kPlayerPlaying, (mPlayingTimeUs+500)/1000 );
if (mRebufferingEvents != 0) {
- mAnalyticsItem->setInt64(kPlayerRebuffering, (mRebufferingTimeUs+500)/1000 );
- mAnalyticsItem->setInt32(kPlayerRebufferingCount, mRebufferingEvents);
- mAnalyticsItem->setInt32(kPlayerRebufferingAtExit, mRebufferingAtExit);
+ mMetricsItem->setInt64(kPlayerRebuffering, (mRebufferingTimeUs+500)/1000 );
+ mMetricsItem->setInt32(kPlayerRebufferingCount, mRebufferingEvents);
+ mMetricsItem->setInt32(kPlayerRebufferingAtExit, mRebufferingAtExit);
}
- mAnalyticsItem->setCString(kPlayerDataSourceType, mPlayer->getDataSourceType());
+ mMetricsItem->setCString(kPlayerDataSourceType, mPlayer->getDataSourceType());
}
@@ -621,7 +619,7 @@
}
ALOGV("logMetrics(%p) from %s at state %d", this, where, mState);
- if (mAnalyticsItem == NULL || mAnalyticsItem->isEnabled() == false) {
+ if (mMetricsItem == NULL || mMetricsItem->isEnabled() == false) {
return;
}
@@ -630,18 +628,18 @@
// and that always injects 3 fields (duration, playing time, and
// datasource) into the record.
// So the canonical "empty" record has 3 elements in it.
- if (mAnalyticsItem->count() > 3) {
+ if (mMetricsItem->count() > 3) {
- mAnalyticsItem->selfrecord();
+ mMetricsItem->selfrecord();
// re-init in case we prepare() and start() again.
- delete mAnalyticsItem ;
- mAnalyticsItem = MediaAnalyticsItem::create(kKeyPlayer);
- if (mAnalyticsItem) {
- mAnalyticsItem->setUid(mClientUid);
+ delete mMetricsItem ;
+ mMetricsItem = mediametrics::Item::create(kKeyPlayer);
+ if (mMetricsItem) {
+ mMetricsItem->setUid(mClientUid);
}
} else {
- ALOGV("nothing to record (only %zu fields)", mAnalyticsItem->count());
+ ALOGV("nothing to record (only %zu fields)", mMetricsItem->count());
}
}
@@ -779,11 +777,11 @@
status_t NuPlayerDriver::getParameter(int key, Parcel *reply) {
- if (key == FOURCC('m','t','r','X') && mAnalyticsItem != NULL) {
+ if (key == FOURCC('m','t','r','X') && mMetricsItem != NULL) {
// mtrX -- a play on 'metrics' (not matrix)
// gather current info all together, parcel it, and send it back
updateMetrics("api");
- mAnalyticsItem->writeToParcel(reply);
+ mMetricsItem->writeToParcel(reply);
return OK;
}
@@ -1007,12 +1005,12 @@
// 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 && mAnalyticsItem != NULL) {
- mAnalyticsItem->setInt32(kPlayerError, ext1);
+ if (msg == MEDIA_ERROR && mMetricsItem != NULL) {
+ mMetricsItem->setInt32(kPlayerError, ext1);
if (ext2 != 0) {
- mAnalyticsItem->setInt32(kPlayerErrorCode, ext2);
+ mMetricsItem->setInt32(kPlayerErrorCode, ext2);
}
- mAnalyticsItem->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 ad878f8..7001f4a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
@@ -16,7 +16,7 @@
#include <media/MediaPlayerInterface.h>
-#include <media/MediaAnalyticsItem.h>
+#include <media/MediaMetricsItem.h>
#include <media/stagefright/foundation/ABase.h>
namespace android {
@@ -141,7 +141,7 @@
sp<AudioSink> mAudioSink;
uint32_t mPlayerFlags;
- MediaAnalyticsItem *mAnalyticsItem;
+ mediametrics::Item *mMetricsItem;
uid_t mClientUid;
bool mAtEOS;
diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
index f21d2b3..14f1323 100644
--- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
@@ -24,7 +24,7 @@
#include "AnotherPacketSource.h"
#include "NuPlayerStreamListener.h"
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
diff --git a/media/libmediatranscoding/.clang-format b/media/libmediatranscoding/.clang-format
new file mode 100644
index 0000000..3198d00
--- /dev/null
+++ b/media/libmediatranscoding/.clang-format
@@ -0,0 +1,29 @@
+#
+# 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.
+#
+
+BasedOnStyle: Google
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: true
+AllowShortLoopsOnASingleLine: true
+BinPackArguments: true
+BinPackParameters: true
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+ContinuationIndentWidth: 8
+DerivePointerAlignment: false
+IndentWidth: 4
+PointerAlignment: Left
+TabWidth: 4
\ No newline at end of file
diff --git a/media/libmediatranscoding/Android.bp b/media/libmediatranscoding/Android.bp
index 8f0e278..7468426 100644
--- a/media/libmediatranscoding/Android.bp
+++ b/media/libmediatranscoding/Android.bp
@@ -1,3 +1,19 @@
+/*
+ * 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.
+ */
+
// AIDL interfaces of MediaTranscoding.
aidl_interface {
name: "mediatranscoding_aidl_interface",
@@ -6,10 +22,46 @@
"aidl/android/media/IMediaTranscodingService.aidl",
"aidl/android/media/ITranscodingServiceClient.aidl",
"aidl/android/media/TranscodingErrorCode.aidl",
+ "aidl/android/media/TranscodingJobPriority.aidl",
"aidl/android/media/TranscodingType.aidl",
"aidl/android/media/TranscodingVideoCodecType.aidl",
- "aidl/android/media/TranscodingJob.aidl",
- "aidl/android/media/TranscodingRequest.aidl",
- "aidl/android/media/TranscodingResult.aidl",
+ "aidl/android/media/TranscodingJobParcel.aidl",
+ "aidl/android/media/TranscodingRequestParcel.aidl",
+ "aidl/android/media/TranscodingResultParcel.aidl",
],
}
+
+cc_library_shared {
+ name: "libmediatranscoding",
+
+ srcs: [
+ "TranscodingClientManager.cpp"
+ ],
+
+ shared_libs: [
+ "libbinder_ndk",
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+
+ export_include_dirs: ["include"],
+
+ static_libs: [
+ "mediatranscoding_aidl_interface-ndk_platform",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wno-error=deprecated-declarations",
+ "-Wall",
+ ],
+
+ sanitize: {
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ cfi: true,
+ },
+}
diff --git a/media/libmediatranscoding/TranscodingClientManager.cpp b/media/libmediatranscoding/TranscodingClientManager.cpp
new file mode 100644
index 0000000..7252437
--- /dev/null
+++ b/media/libmediatranscoding/TranscodingClientManager.cpp
@@ -0,0 +1,145 @@
+/*
+ * 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 "TranscodingClientManager"
+
+#include <inttypes.h>
+#include <media/TranscodingClientManager.h>
+#include <utils/Log.h>
+
+namespace android {
+
+using Status = ::ndk::ScopedAStatus;
+
+// static
+TranscodingClientManager& TranscodingClientManager::getInstance() {
+ static TranscodingClientManager gInstance{};
+ return gInstance;
+}
+
+// static
+void TranscodingClientManager::BinderDiedCallback(void* cookie) {
+ int32_t clientId = static_cast<int32_t>(reinterpret_cast<intptr_t>(cookie));
+ ALOGD("Client %" PRId32 " is dead", clientId);
+ // Don't check for pid validity since we know it's already dead.
+ TranscodingClientManager& manager = TranscodingClientManager::getInstance();
+ manager.removeClient(clientId);
+}
+
+TranscodingClientManager::TranscodingClientManager()
+ : mDeathRecipient(AIBinder_DeathRecipient_new(BinderDiedCallback)) {
+ ALOGD("TranscodingClientManager started");
+}
+
+TranscodingClientManager::~TranscodingClientManager() {
+ ALOGD("TranscodingClientManager exited");
+}
+
+bool TranscodingClientManager::isClientIdRegistered(int32_t clientId) const {
+ std::scoped_lock lock{mLock};
+ return mClientIdToClientInfoMap.find(clientId) != mClientIdToClientInfoMap.end();
+}
+
+void TranscodingClientManager::dumpAllClients(int fd, const Vector<String16>& args __unused) {
+ String8 result;
+
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+
+ snprintf(buffer, SIZE, " Total num of Clients: %zu\n", mClientIdToClientInfoMap.size());
+ result.append(buffer);
+
+ if (mClientIdToClientInfoMap.size() > 0) {
+ snprintf(buffer, SIZE, "========== Dumping all clients =========\n");
+ result.append(buffer);
+ }
+
+ for (const auto& iter : mClientIdToClientInfoMap) {
+ const std::shared_ptr<ITranscodingServiceClient> client = iter.second->mClient;
+ std::string clientName;
+ Status status = client->getName(&clientName);
+ if (!status.isOk()) {
+ ALOGE("Failed to get client: %d information", iter.first);
+ continue;
+ }
+ snprintf(buffer, SIZE, " -- Clients: %d name: %s\n", iter.first, clientName.c_str());
+ result.append(buffer);
+ }
+
+ write(fd, result.string(), result.size());
+}
+
+status_t TranscodingClientManager::addClient(std::unique_ptr<ClientInfo> client) {
+ // Validate the client.
+ if (client == nullptr || client->mClientId < 0 || client->mClientPid < 0 ||
+ client->mClientUid < 0 || client->mClientOpPackageName.empty() ||
+ client->mClientOpPackageName == "") {
+ ALOGE("Invalid client");
+ return BAD_VALUE;
+ }
+
+ std::scoped_lock lock{mLock};
+
+ // Check if the client already exists.
+ if (mClientIdToClientInfoMap.count(client->mClientId) != 0) {
+ ALOGW("Client already exists.");
+ return ALREADY_EXISTS;
+ }
+
+ ALOGD("Adding client id %d pid: %d uid: %d %s", client->mClientId, client->mClientPid,
+ client->mClientUid, client->mClientOpPackageName.c_str());
+
+ AIBinder_linkToDeath(client->mClient->asBinder().get(), mDeathRecipient.get(),
+ reinterpret_cast<void*>(client->mClientId));
+
+ // Adds the new client to the map.
+ mClientIdToClientInfoMap[client->mClientId] = std::move(client);
+
+ return OK;
+}
+
+status_t TranscodingClientManager::removeClient(int32_t clientId) {
+ ALOGD("Removing client id %d", clientId);
+ std::scoped_lock lock{mLock};
+
+ // Checks if the client is valid.
+ auto it = mClientIdToClientInfoMap.find(clientId);
+ if (it == mClientIdToClientInfoMap.end()) {
+ ALOGE("Client id %d does not exist", clientId);
+ return INVALID_OPERATION;
+ }
+
+ std::shared_ptr<ITranscodingServiceClient> client = it->second->mClient;
+
+ // Check if the client still live. If alive, unlink the death.
+ if (client) {
+ AIBinder_unlinkToDeath(client->asBinder().get(), mDeathRecipient.get(),
+ reinterpret_cast<void*>(clientId));
+ }
+
+ // Erase the entry.
+ mClientIdToClientInfoMap.erase(it);
+
+ return OK;
+}
+
+size_t TranscodingClientManager::getNumOfClients() const {
+ std::scoped_lock lock{mLock};
+ return mClientIdToClientInfoMap.size();
+}
+
+} // namespace android
diff --git a/media/libmediatranscoding/aidl/android/media/IMediaTranscodingService.aidl b/media/libmediatranscoding/aidl/android/media/IMediaTranscodingService.aidl
index cae8ff0..07b6c1a 100644
--- a/media/libmediatranscoding/aidl/android/media/IMediaTranscodingService.aidl
+++ b/media/libmediatranscoding/aidl/android/media/IMediaTranscodingService.aidl
@@ -16,8 +16,8 @@
package android.media;
-import android.media.TranscodingJob;
-import android.media.TranscodingRequest;
+import android.media.TranscodingJobParcel;
+import android.media.TranscodingRequestParcel;
import android.media.ITranscodingServiceClient;
/**
@@ -27,6 +27,25 @@
*/
interface IMediaTranscodingService {
/**
+ * All MediaTranscoding service and device Binder calls may return a
+ * ServiceSpecificException with the following error codes
+ */
+ const int ERROR_PERMISSION_DENIED = 1;
+ const int ERROR_ALREADY_EXISTS = 2;
+ const int ERROR_ILLEGAL_ARGUMENT = 3;
+ const int ERROR_DISCONNECTED = 4;
+ const int ERROR_TIMED_OUT = 5;
+ const int ERROR_DISABLED = 6;
+ const int ERROR_INVALID_OPERATION = 7;
+
+ /**
+ * Default UID/PID values for non-privileged callers of
+ * registerClient().
+ */
+ const int USE_CALLING_UID = -1;
+ const int USE_CALLING_PID = -1;
+
+ /**
* Register the client with the MediaTranscodingService.
*
* Client must call this function to register itself with the service in order to perform
@@ -34,10 +53,16 @@
* Client should save this Id and use it for all the transaction with the service.
*
* @param client interface for the MediaTranscodingService to call the client.
+ * @param opPackageName op package name of the client.
+ * @param clientUid user id of the client.
+ * @param clientPid process id of the client.
* @return a unique positive Id assigned to the client by the service, -1 means failed to
* register.
*/
- int registerClient(in ITranscodingServiceClient client);
+ int registerClient(in ITranscodingServiceClient client,
+ in String opPackageName,
+ in int clientUid,
+ in int clientPid);
/**
* Unregister the client with the MediaTranscodingService.
@@ -50,6 +75,11 @@
boolean unregisterClient(in int clientId);
/**
+ * Returns the number of clients. This is used for debugging.
+ */
+ int getNumOfClients();
+
+ /**
* Submits a transcoding request to MediaTranscodingService.
*
* @param clientId assigned Id of the client.
@@ -58,8 +88,8 @@
* @return a unique positive jobId generated by the MediaTranscodingService, -1 means failure.
*/
int submitRequest(in int clientId,
- in TranscodingRequest request,
- out TranscodingJob job);
+ in TranscodingRequestParcel request,
+ out TranscodingJobParcel job);
/**
* Cancels a transcoding job.
@@ -77,5 +107,5 @@
* @param job(output variable) the TranscodingJob associated with the jobId.
* @return true if succeeds, false otherwise.
*/
- boolean getJobWithId(in int jobId, out TranscodingJob job);
+ boolean getJobWithId(in int jobId, out TranscodingJobParcel job);
}
diff --git a/media/libmediatranscoding/aidl/android/media/ITranscodingServiceClient.aidl b/media/libmediatranscoding/aidl/android/media/ITranscodingServiceClient.aidl
index 29b6f35..e23c833 100644
--- a/media/libmediatranscoding/aidl/android/media/ITranscodingServiceClient.aidl
+++ b/media/libmediatranscoding/aidl/android/media/ITranscodingServiceClient.aidl
@@ -17,8 +17,8 @@
package android.media;
import android.media.TranscodingErrorCode;
-import android.media.TranscodingJob;
-import android.media.TranscodingResult;
+import android.media.TranscodingJobParcel;
+import android.media.TranscodingResultParcel;
/**
* ITranscodingServiceClient interface for the MediaTranscodingervice to communicate with the
@@ -39,7 +39,7 @@
* @param jobId jobId assigned by the MediaTranscodingService upon receiving request.
* @param result contains the transcoded file stats and other transcoding metrics if requested.
*/
- oneway void onTranscodingFinished(in int jobId, in TranscodingResult result);
+ oneway void onTranscodingFinished(in int jobId, in TranscodingResultParcel result);
/**
* Called when the transcoding associated with the jobId failed.
@@ -60,7 +60,9 @@
* @param oldAwaitNumber previous number of jobs ahead of current job.
* @param newAwaitNumber updated number of jobs ahead of current job.
*/
- oneway void onAwaitNumberOfJobsChanged(in int jobId, in int oldAwaitNumber, in int newAwaitNumber);
+ oneway void onAwaitNumberOfJobsChanged(in int jobId,
+ in int oldAwaitNumber,
+ in int newAwaitNumber);
/**
* Called when there is an update on the progress of the TranscodingJob.
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingJob.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingJobParcel.aidl
similarity index 91%
rename from media/libmediatranscoding/aidl/android/media/TranscodingJob.aidl
rename to media/libmediatranscoding/aidl/android/media/TranscodingJobParcel.aidl
index 42ad8ad..d912c38 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingJob.aidl
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingJobParcel.aidl
@@ -16,7 +16,7 @@
package android.media;
-import android.media.TranscodingRequest;
+import android.media.TranscodingRequestParcel;
/**
* TranscodingJob is generated by the MediaTranscodingService upon receiving a TranscodingRequest.
@@ -26,7 +26,7 @@
* {@hide}
*/
//TODO(hkuang): Implement the parcelable.
-parcelable TranscodingJob {
+parcelable TranscodingJobParcel {
/**
* A unique positive Id generated by the MediaTranscodingService.
*/
@@ -35,7 +35,7 @@
/**
* The request associated with the TranscodingJob.
*/
- TranscodingRequest request;
+ TranscodingRequestParcel request;
/**
* Current number of jobs ahead of this job. The service schedules the job based on the priority
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
new file mode 100644
index 0000000..1a5d81a
--- /dev/null
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package android.media;
+
+/**
+ * Priority of a transcoding job.
+ *
+ * {@hide}
+ */
+@Backing(type="int")
+enum TranscodingJobPriority {
+ // TODO(hkuang): define what each priority level actually mean.
+ kUnspecified = 0,
+ kLow = 1,
+ /**
+ * 2 ~ 20 is reserved for future use.
+ */
+ kNormal = 21,
+ /**
+ * 22 ~ 30 is reserved for future use.
+ */
+ kHigh = 31,
+}
\ No newline at end of file
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingRequest.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingRequestParcel.aidl
similarity index 91%
rename from media/libmediatranscoding/aidl/android/media/TranscodingRequest.aidl
rename to media/libmediatranscoding/aidl/android/media/TranscodingRequestParcel.aidl
index 6b51ee3..7b7986d 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingRequest.aidl
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingRequestParcel.aidl
@@ -16,6 +16,7 @@
package android.media;
+import android.media.TranscodingJobPriority;
import android.media.TranscodingType;
/**
@@ -24,7 +25,7 @@
* {@hide}
*/
//TODO(hkuang): Implement the parcelable.
-parcelable TranscodingRequest {
+parcelable TranscodingRequestParcel {
/**
* Name of file to be transcoded.
*/
@@ -48,8 +49,7 @@
/**
* Priority of this transcoding. Service will schedule the transcoding based on the priority.
*/
- // TODO(hkuang): Define the priority level.
- int priority;
+ TranscodingJobPriority priority;
/**
* Whether to receive update on progress and change of awaitNumJobs.
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingResult.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingResultParcel.aidl
similarity index 93%
rename from media/libmediatranscoding/aidl/android/media/TranscodingResult.aidl
rename to media/libmediatranscoding/aidl/android/media/TranscodingResultParcel.aidl
index a41e025..65c49e7 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingResult.aidl
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingResultParcel.aidl
@@ -16,15 +16,13 @@
package android.media;
-import android.media.TranscodingJob;
-
/**
* Result of the transcoding.
*
* {@hide}
*/
//TODO(hkuang): Implement the parcelable.
-parcelable TranscodingResult {
+parcelable TranscodingResultParcel {
/**
* The jobId associated with the TranscodingResult.
*/
diff --git a/media/libmediatranscoding/include/media/TranscodingClientManager.h b/media/libmediatranscoding/include/media/TranscodingClientManager.h
new file mode 100644
index 0000000..eec120a
--- /dev/null
+++ b/media/libmediatranscoding/include/media/TranscodingClientManager.h
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_MEDIA_TRANSCODING_CLIENT_MANAGER_H
+#define ANDROID_MEDIA_TRANSCODING_CLIENT_MANAGER_H
+
+#include <aidl/android/media/BnTranscodingServiceClient.h>
+#include <android/binder_ibinder.h>
+#include <sys/types.h>
+#include <utils/Condition.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include <mutex>
+#include <unordered_map>
+
+namespace android {
+
+using ::aidl::android::media::ITranscodingServiceClient;
+
+class MediaTranscodingService;
+
+/*
+ * TranscodingClientManager manages all the transcoding clients across different processes.
+ *
+ * TranscodingClientManager is a global singleton that could only acquired by
+ * MediaTranscodingService. It manages all the clients's registration/unregistration and clients'
+ * information. It also bookkeeps all the clients' information. It also monitors to the death of the
+ * clients. Upon client's death, it will remove the client from it.
+ *
+ * TODO(hkuang): Hook up with ResourceManager for resource management.
+ * TODO(hkuang): Hook up with MediaMetrics to log all the transactions.
+ */
+class TranscodingClientManager {
+ public:
+ virtual ~TranscodingClientManager();
+
+ /**
+ * ClientInfo contains a single client's information.
+ */
+ struct ClientInfo {
+ /* The remote client that this ClientInfo is associated with. */
+ std::shared_ptr<ITranscodingServiceClient> mClient;
+ /* A unique positive Id assigned to the client by the service. */
+ int32_t mClientId;
+ /* Process id of the client */
+ int32_t mClientPid;
+ /* User id of the client. */
+ int32_t mClientUid;
+ /* Package name of the client. */
+ std::string mClientOpPackageName;
+
+ ClientInfo(const std::shared_ptr<ITranscodingServiceClient>& client, int64_t clientId,
+ int32_t pid, int32_t uid, const std::string& opPackageName)
+ : mClient(client),
+ mClientId(clientId),
+ mClientPid(pid),
+ mClientUid(uid),
+ mClientOpPackageName(opPackageName) {}
+ };
+
+ /**
+ * Adds a new client to the manager.
+ *
+ * The client must have valid clientId, pid, uid and opPackageName, otherwise, this will return
+ * a non-zero errorcode. If the client has already been added, it will also return non-zero
+ * errorcode.
+ *
+ * @param client to be added to the manager.
+ * @return 0 if client is added successfully, non-zero errorcode otherwise.
+ */
+ status_t addClient(std::unique_ptr<ClientInfo> client);
+
+ /**
+ * Removes an existing client from the manager.
+ *
+ * If the client does not exist, this will return non-zero errorcode.
+ *
+ * @param clientId id of the client to be removed..
+ * @return 0 if client is removed successfully, non-zero errorcode otherwise.
+ */
+ status_t removeClient(int32_t clientId);
+
+ /**
+ * Gets the number of clients.
+ */
+ size_t getNumOfClients() const;
+
+ /**
+ * Checks if a client with clientId is already registered.
+ */
+ bool isClientIdRegistered(int32_t clientId) const;
+
+ /**
+ * Dump all the client information to the fd.
+ */
+ void dumpAllClients(int fd, const Vector<String16>& args);
+
+ private:
+ friend class MediaTranscodingService;
+ friend class TranscodingClientManagerTest;
+
+ /** Get the singleton instance of the TranscodingClientManager. */
+ static TranscodingClientManager& getInstance();
+
+ TranscodingClientManager();
+
+ static void BinderDiedCallback(void* cookie);
+
+ mutable std::mutex mLock;
+ std::unordered_map<int32_t, std::unique_ptr<ClientInfo>> mClientIdToClientInfoMap
+ GUARDED_BY(mLock);
+
+ ::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
+};
+
+} // namespace android
+#endif // ANDROID_MEDIA_TRANSCODING_SERVICE_H
diff --git a/media/libmediatranscoding/tests/Android.bp b/media/libmediatranscoding/tests/Android.bp
new file mode 100644
index 0000000..f3cc4c5
--- /dev/null
+++ b/media/libmediatranscoding/tests/Android.bp
@@ -0,0 +1,38 @@
+// Build the unit tests for libmediatranscoding.
+cc_defaults {
+ name: "libmediatranscoding_test_defaults",
+
+ header_libs: [
+ "libbase_headers",
+ "libmedia_headers",
+ ],
+
+ shared_libs: [
+ "libbinder_ndk",
+ "libcutils",
+ "liblog",
+ "libutils",
+ "libmediatranscoding"
+ ],
+
+ static_libs: [
+ "mediatranscoding_aidl_interface-ndk_platform",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ test_suites: ["device-tests"],
+}
+
+//
+// TranscodingClientManager unit test
+//
+cc_test {
+ name: "TranscodingClientManager_tests",
+ defaults: ["libmediatranscoding_test_defaults"],
+
+ srcs: ["TranscodingClientManager_tests.cpp"],
+}
\ No newline at end of file
diff --git a/media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp b/media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp
new file mode 100644
index 0000000..5d2419d
--- /dev/null
+++ b/media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp
@@ -0,0 +1,280 @@
+/*
+ * 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.
+ */
+
+// Unit Test for TranscodingClientManager
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "TranscodingClientManagerTest"
+
+#include <aidl/android/media/BnTranscodingServiceClient.h>
+#include <aidl/android/media/IMediaTranscodingService.h>
+#include <aidl/android/media/ITranscodingServiceClient.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <gtest/gtest.h>
+#include <media/TranscodingClientManager.h>
+#include <utils/Log.h>
+
+namespace android {
+
+using Status = ::ndk::ScopedAStatus;
+using aidl::android::media::BnTranscodingServiceClient;
+using aidl::android::media::IMediaTranscodingService;
+using aidl::android::media::ITranscodingServiceClient;
+
+constexpr int32_t kInvalidClientId = -1;
+constexpr int32_t kInvalidClientPid = -1;
+constexpr int32_t kInvalidClientUid = -1;
+constexpr const char* kInvalidClientOpPackageName = "";
+
+constexpr int32_t kClientId = 1;
+constexpr int32_t kClientPid = 2;
+constexpr int32_t kClientUid = 3;
+constexpr const char* kClientOpPackageName = "TestClient";
+
+struct TestClient : public BnTranscodingServiceClient {
+ TestClient(const std::shared_ptr<IMediaTranscodingService>& service) : mService(service) {
+ ALOGD("TestClient Created");
+ }
+
+ Status getName(std::string* _aidl_return) override {
+ *_aidl_return = "test_client";
+ return Status::ok();
+ }
+
+ Status onTranscodingFinished(
+ int32_t /* in_jobId */,
+ const ::aidl::android::media::TranscodingResultParcel& /* in_result */) override {
+ return Status::ok();
+ }
+
+ Status onTranscodingFailed(
+ int32_t /* in_jobId */,
+ ::aidl::android::media::TranscodingErrorCode /*in_errorCode */) override {
+ return Status::ok();
+ }
+
+ Status onAwaitNumberOfJobsChanged(int32_t /* in_jobId */, int32_t /* in_oldAwaitNumber */,
+ int32_t /* in_newAwaitNumber */) override {
+ return Status::ok();
+ }
+
+ Status onProgressUpdate(int32_t /* in_jobId */, int32_t /* in_progress */) override {
+ return Status::ok();
+ }
+
+ virtual ~TestClient() { ALOGI("TestClient destroyed"); };
+
+ private:
+ std::shared_ptr<IMediaTranscodingService> mService;
+ TestClient(const TestClient&) = delete;
+ TestClient& operator=(const TestClient&) = delete;
+};
+
+class TranscodingClientManagerTest : public ::testing::Test {
+ public:
+ TranscodingClientManagerTest() : mClientManager(TranscodingClientManager::getInstance()) {
+ ALOGD("TranscodingClientManagerTest created");
+ }
+
+ void SetUp() override {
+ ::ndk::SpAIBinder binder(AServiceManager_getService("media.transcoding"));
+ mService = IMediaTranscodingService::fromBinder(binder);
+ if (mService == nullptr) {
+ ALOGE("Failed to connect to the media.trascoding service.");
+ return;
+ }
+
+ mTestClient = ::ndk::SharedRefBase::make<TestClient>(mService);
+ }
+
+ void TearDown() override {
+ ALOGI("TranscodingClientManagerTest tear down");
+ mService = nullptr;
+ }
+
+ ~TranscodingClientManagerTest() { ALOGD("TranscodingClientManagerTest destroyed"); }
+
+ TranscodingClientManager& mClientManager;
+ std::shared_ptr<ITranscodingServiceClient> mTestClient = nullptr;
+ std::shared_ptr<IMediaTranscodingService> mService = nullptr;
+};
+
+TEST_F(TranscodingClientManagerTest, TestAddingWithInvalidClientId) {
+ std::shared_ptr<ITranscodingServiceClient> client =
+ ::ndk::SharedRefBase::make<TestClient>(mService);
+
+ // Create a client with invalid client id.
+ std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo =
+ std::make_unique<TranscodingClientManager::ClientInfo>(
+ client, kInvalidClientId, kClientPid, kClientUid, kClientOpPackageName);
+
+ // Add the client to the manager and expect failure.
+ status_t err = mClientManager.addClient(std::move(clientInfo));
+ EXPECT_TRUE(err != OK);
+}
+
+TEST_F(TranscodingClientManagerTest, TestAddingWithInvalidClientPid) {
+ std::shared_ptr<ITranscodingServiceClient> client =
+ ::ndk::SharedRefBase::make<TestClient>(mService);
+
+ // Create a client with invalid Pid.
+ std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo =
+ std::make_unique<TranscodingClientManager::ClientInfo>(
+ client, kClientId, kInvalidClientPid, kClientUid, kClientOpPackageName);
+
+ // Add the client to the manager and expect failure.
+ status_t err = mClientManager.addClient(std::move(clientInfo));
+ EXPECT_TRUE(err != OK);
+}
+
+TEST_F(TranscodingClientManagerTest, TestAddingWithInvalidClientUid) {
+ std::shared_ptr<ITranscodingServiceClient> client =
+ ::ndk::SharedRefBase::make<TestClient>(mService);
+
+ // Create a client with invalid Uid.
+ std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo =
+ std::make_unique<TranscodingClientManager::ClientInfo>(
+ client, kClientId, kClientPid, kInvalidClientUid, kClientOpPackageName);
+
+ // Add the client to the manager and expect failure.
+ status_t err = mClientManager.addClient(std::move(clientInfo));
+ EXPECT_TRUE(err != OK);
+}
+
+TEST_F(TranscodingClientManagerTest, TestAddingWithInvalidClientPackageName) {
+ std::shared_ptr<ITranscodingServiceClient> client =
+ ::ndk::SharedRefBase::make<TestClient>(mService);
+
+ // Create a client with invalid packagename.
+ std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo =
+ std::make_unique<TranscodingClientManager::ClientInfo>(
+ client, kClientId, kClientPid, kClientUid, kInvalidClientOpPackageName);
+
+ // Add the client to the manager and expect failure.
+ status_t err = mClientManager.addClient(std::move(clientInfo));
+ EXPECT_TRUE(err != OK);
+}
+
+TEST_F(TranscodingClientManagerTest, TestAddingValidClient) {
+ std::shared_ptr<ITranscodingServiceClient> client1 =
+ ::ndk::SharedRefBase::make<TestClient>(mService);
+
+ std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo =
+ std::make_unique<TranscodingClientManager::ClientInfo>(
+ client1, kClientId, kClientPid, kClientUid, kClientOpPackageName);
+
+ status_t err = mClientManager.addClient(std::move(clientInfo));
+ EXPECT_TRUE(err == OK);
+
+ size_t numOfClients = mClientManager.getNumOfClients();
+ EXPECT_EQ(numOfClients, 1);
+
+ err = mClientManager.removeClient(kClientId);
+ EXPECT_TRUE(err == OK);
+}
+
+TEST_F(TranscodingClientManagerTest, TestAddingDupliacteClient) {
+ std::shared_ptr<ITranscodingServiceClient> client1 =
+ ::ndk::SharedRefBase::make<TestClient>(mService);
+
+ std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo =
+ std::make_unique<TranscodingClientManager::ClientInfo>(
+ client1, kClientId, kClientPid, kClientUid, kClientOpPackageName);
+
+ status_t err = mClientManager.addClient(std::move(clientInfo));
+ EXPECT_TRUE(err == OK);
+
+ err = mClientManager.addClient(std::move(clientInfo));
+ EXPECT_TRUE(err != OK);
+
+ err = mClientManager.removeClient(kClientId);
+ EXPECT_TRUE(err == OK);
+}
+
+TEST_F(TranscodingClientManagerTest, TestAddingMultipleClient) {
+ std::shared_ptr<ITranscodingServiceClient> client1 =
+ ::ndk::SharedRefBase::make<TestClient>(mService);
+
+ std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo1 =
+ std::make_unique<TranscodingClientManager::ClientInfo>(
+ client1, kClientId, kClientPid, kClientUid, kClientOpPackageName);
+
+ status_t err = mClientManager.addClient(std::move(clientInfo1));
+ EXPECT_TRUE(err == OK);
+
+ std::shared_ptr<ITranscodingServiceClient> client2 =
+ ::ndk::SharedRefBase::make<TestClient>(mService);
+
+ std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo2 =
+ std::make_unique<TranscodingClientManager::ClientInfo>(
+ client2, kClientId + 1, kClientPid, kClientUid, kClientOpPackageName);
+
+ err = mClientManager.addClient(std::move(clientInfo2));
+ EXPECT_TRUE(err == OK);
+
+ std::shared_ptr<ITranscodingServiceClient> client3 =
+ ::ndk::SharedRefBase::make<TestClient>(mService);
+
+ // Create a client with invalid packagename.
+ std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo3 =
+ std::make_unique<TranscodingClientManager::ClientInfo>(
+ client3, kClientId + 2, kClientPid, kClientUid, kClientOpPackageName);
+
+ err = mClientManager.addClient(std::move(clientInfo3));
+ EXPECT_TRUE(err == OK);
+
+ size_t numOfClients = mClientManager.getNumOfClients();
+ EXPECT_EQ(numOfClients, 3);
+
+ err = mClientManager.removeClient(kClientId);
+ EXPECT_TRUE(err == OK);
+
+ err = mClientManager.removeClient(kClientId + 1);
+ EXPECT_TRUE(err == OK);
+
+ err = mClientManager.removeClient(kClientId + 2);
+ EXPECT_TRUE(err == OK);
+}
+
+TEST_F(TranscodingClientManagerTest, TestRemovingNonExistClient) {
+ status_t err = mClientManager.removeClient(kInvalidClientId);
+ EXPECT_TRUE(err != OK);
+
+ err = mClientManager.removeClient(1000 /* clientId */);
+ EXPECT_TRUE(err != OK);
+}
+
+TEST_F(TranscodingClientManagerTest, TestCheckClientWithClientId) {
+ std::shared_ptr<ITranscodingServiceClient> client =
+ ::ndk::SharedRefBase::make<TestClient>(mService);
+
+ std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo =
+ std::make_unique<TranscodingClientManager::ClientInfo>(
+ client, kClientId, kClientPid, kClientUid, kClientOpPackageName);
+
+ status_t err = mClientManager.addClient(std::move(clientInfo));
+ EXPECT_TRUE(err == OK);
+
+ bool res = mClientManager.isClientIdRegistered(kClientId);
+ EXPECT_TRUE(res);
+
+ res = mClientManager.isClientIdRegistered(kInvalidClientId);
+ EXPECT_FALSE(res);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/media/libmediatranscoding/tests/build_and_run_all_unit_tests.sh b/media/libmediatranscoding/tests/build_and_run_all_unit_tests.sh
new file mode 100644
index 0000000..9832696
--- /dev/null
+++ b/media/libmediatranscoding/tests/build_and_run_all_unit_tests.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Run tests in this directory.
+#
+
+if [ -z "$ANDROID_BUILD_TOP" ]; then
+ echo "Android build environment not set"
+ exit -1
+fi
+
+# ensure we have mm
+. $ANDROID_BUILD_TOP/build/envsetup.sh
+
+mm
+
+echo "waiting for device"
+
+adb root && adb wait-for-device remount && adb sync
+
+echo "========================================"
+
+echo "testing TranscodingClientManager"
+adb shell /data/nativetest64/TranscodingClientManager_tests/TranscodingClientManager_tests
diff --git a/media/libnblog/ReportPerformance.cpp b/media/libnblog/ReportPerformance.cpp
index b050b83..aa678ba 100644
--- a/media/libnblog/ReportPerformance.cpp
+++ b/media/libnblog/ReportPerformance.cpp
@@ -30,7 +30,7 @@
#include <sys/time.h>
#include <utility>
#include <json/json.h>
-#include <media/MediaAnalyticsItem.h>
+#include <media/MediaMetricsItem.h>
#include <media/nblog/Events.h>
#include <media/nblog/PerformanceAnalysis.h>
#include <media/nblog/ReportPerformance.h>
@@ -168,7 +168,7 @@
return false;
}
- std::unique_ptr<MediaAnalyticsItem> item(MediaAnalyticsItem::create("audiothread"));
+ std::unique_ptr<mediametrics::Item> item(mediametrics::Item::create("audiothread"));
const Histogram &workHist = data.workHist;
if (workHist.totalCount() > 0) {
diff --git a/media/libstagefright/AACWriter.cpp b/media/libstagefright/AACWriter.cpp
index e173974..9c0a7a5 100644
--- a/media/libstagefright/AACWriter.cpp
+++ b/media/libstagefright/AACWriter.cpp
@@ -31,7 +31,7 @@
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/mediarecorder.h>
namespace android {
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index ef9d253..960120f 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -1310,7 +1310,7 @@
OMX_U32 bufferCount, bufferSize, minUndequeuedBuffers;
status_t err = configureOutputBuffersFromNativeWindow(
&bufferCount, &bufferSize, &minUndequeuedBuffers,
- false /* preregister */);
+ mFlags & kFlagPreregisterMetadataBuffers /* preregister */);
if (err != OK)
return err;
mNumUndequeuedBuffers = minUndequeuedBuffers;
@@ -1896,6 +1896,19 @@
setPortMode(kPortIndexInput, IOMX::kPortModePresetByteBuffer);
err = OK; // ignore error for now
}
+
+ OMX_INDEXTYPE index;
+ if (mOMXNode->getExtensionIndex(
+ "OMX.google.android.index.preregisterMetadataBuffers", &index) == OK) {
+ OMX_CONFIG_BOOLEANTYPE param;
+ InitOMXParams(¶m);
+ param.bEnabled = OMX_FALSE;
+ if (mOMXNode->getParameter(index, ¶m, sizeof(param)) == OK) {
+ if (param.bEnabled == OMX_TRUE) {
+ mFlags |= kFlagPreregisterMetadataBuffers;
+ }
+ }
+ }
}
if (haveNativeWindow) {
sp<ANativeWindow> nativeWindow =
diff --git a/media/libstagefright/ACodecBufferChannel.cpp b/media/libstagefright/ACodecBufferChannel.cpp
index bf8f09c..e5115d9 100644
--- a/media/libstagefright/ACodecBufferChannel.cpp
+++ b/media/libstagefright/ACodecBufferChannel.cpp
@@ -20,6 +20,8 @@
#include <numeric>
+#include <C2Buffer.h>
+
#include <android/hardware/cas/native/1.0/IDescrambler.h>
#include <android/hardware/drm/1.0/types.h>
#include <binder/MemoryDealer.h>
@@ -250,6 +252,178 @@
return OK;
}
+status_t ACodecBufferChannel::attachBuffer(
+ const std::shared_ptr<C2Buffer> &c2Buffer,
+ const sp<MediaCodecBuffer> &buffer) {
+ switch (c2Buffer->data().type()) {
+ case C2BufferData::LINEAR: {
+ if (c2Buffer->data().linearBlocks().size() != 1u) {
+ return -ENOSYS;
+ }
+ C2ConstLinearBlock block{c2Buffer->data().linearBlocks().front()};
+ C2ReadView view{block.map().get()};
+ if (view.capacity() > buffer->capacity()) {
+ return -ENOSYS;
+ }
+ memcpy(buffer->base(), view.data(), view.capacity());
+ buffer->setRange(0, view.capacity());
+ break;
+ }
+ case C2BufferData::GRAPHIC: {
+ // TODO
+ return -ENOSYS;
+ }
+ case C2BufferData::LINEAR_CHUNKS: [[fallthrough]];
+ case C2BufferData::GRAPHIC_CHUNKS: [[fallthrough]];
+ default:
+ return -ENOSYS;
+ }
+
+ return OK;
+}
+
+int32_t ACodecBufferChannel::getHeapSeqNum(const sp<HidlMemory> &memory) {
+ CHECK(mCrypto);
+ auto it = mHeapSeqNumMap.find(memory);
+ int32_t heapSeqNum = -1;
+ if (it == mHeapSeqNumMap.end()) {
+ heapSeqNum = mCrypto->setHeap(memory);
+ mHeapSeqNumMap.emplace(memory, heapSeqNum);
+ } else {
+ heapSeqNum = it->second;
+ }
+ return heapSeqNum;
+}
+
+status_t ACodecBufferChannel::attachEncryptedBuffer(
+ const sp<hardware::HidlMemory> &memory,
+ bool secure,
+ const uint8_t *key,
+ const uint8_t *iv,
+ CryptoPlugin::Mode mode,
+ CryptoPlugin::Pattern pattern,
+ size_t offset,
+ const CryptoPlugin::SubSample *subSamples,
+ size_t numSubSamples,
+ const sp<MediaCodecBuffer> &buffer) {
+ std::shared_ptr<const std::vector<const BufferInfo>> array(
+ std::atomic_load(&mInputBuffers));
+ BufferInfoIterator it = findClientBuffer(array, buffer);
+ if (it == array->end()) {
+ return -ENOENT;
+ }
+
+ native_handle_t *secureHandle = NULL;
+ if (secure) {
+ sp<SecureBuffer> secureData =
+ static_cast<SecureBuffer *>(it->mCodecBuffer.get());
+ if (secureData->getDestinationType() != ICrypto::kDestinationTypeNativeHandle) {
+ return BAD_VALUE;
+ }
+ secureHandle = static_cast<native_handle_t *>(secureData->getDestinationPointer());
+ }
+ size_t size = 0;
+ for (size_t i = 0; i < numSubSamples; ++i) {
+ size += subSamples[i].mNumBytesOfClearData + subSamples[i].mNumBytesOfEncryptedData;
+ }
+ ssize_t result = -1;
+ ssize_t codecDataOffset = 0;
+ if (mCrypto != NULL) {
+ AString errorDetailMsg;
+ hardware::drm::V1_0::DestinationBuffer destination;
+ if (secure) {
+ destination.type = DrmBufferType::NATIVE_HANDLE;
+ destination.secureMemory = hidl_handle(secureHandle);
+ } else {
+ destination.type = DrmBufferType::SHARED_MEMORY;
+ IMemoryToSharedBuffer(
+ mDecryptDestination, mHeapSeqNum, &destination.nonsecureMemory);
+ }
+
+ int32_t heapSeqNum = getHeapSeqNum(memory);
+ hardware::drm::V1_0::SharedBuffer source{(uint32_t)heapSeqNum, offset, size};
+
+ result = mCrypto->decrypt(key, iv, mode, pattern,
+ source, it->mClientBuffer->offset(),
+ subSamples, numSubSamples, destination, &errorDetailMsg);
+
+ if (result < 0) {
+ return result;
+ }
+
+ if (destination.type == DrmBufferType::SHARED_MEMORY) {
+ memcpy(it->mCodecBuffer->base(), mDecryptDestination->unsecurePointer(), result);
+ }
+ } else {
+ // Here we cast CryptoPlugin::SubSample to hardware::cas::native::V1_0::SubSample
+ // directly, the structure definitions should match as checked in DescramblerImpl.cpp.
+ hidl_vec<SubSample> hidlSubSamples;
+ hidlSubSamples.setToExternal((SubSample *)subSamples, numSubSamples, false /*own*/);
+
+ hardware::cas::native::V1_0::SharedBuffer srcBuffer = {
+ .heapBase = *memory,
+ .offset = (uint64_t) offset,
+ .size = size
+ };
+
+ DestinationBuffer dstBuffer;
+ if (secure) {
+ dstBuffer.type = BufferType::NATIVE_HANDLE;
+ dstBuffer.secureMemory = hidl_handle(secureHandle);
+ } else {
+ dstBuffer.type = BufferType::SHARED_MEMORY;
+ dstBuffer.nonsecureMemory = srcBuffer;
+ }
+
+ Status status = Status::OK;
+ hidl_string detailedError;
+ ScramblingControl sctrl = ScramblingControl::UNSCRAMBLED;
+
+ if (key != NULL) {
+ sctrl = (ScramblingControl)key[0];
+ // Adjust for the PES offset
+ codecDataOffset = key[2] | (key[3] << 8);
+ }
+
+ auto returnVoid = mDescrambler->descramble(
+ sctrl,
+ hidlSubSamples,
+ srcBuffer,
+ 0,
+ dstBuffer,
+ 0,
+ [&status, &result, &detailedError] (
+ Status _status, uint32_t _bytesWritten,
+ const hidl_string& _detailedError) {
+ status = _status;
+ result = (ssize_t)_bytesWritten;
+ detailedError = _detailedError;
+ });
+
+ if (!returnVoid.isOk() || status != Status::OK || result < 0) {
+ ALOGE("descramble failed, trans=%s, status=%d, result=%zd",
+ returnVoid.description().c_str(), status, result);
+ return UNKNOWN_ERROR;
+ }
+
+ if (result < codecDataOffset) {
+ ALOGD("invalid codec data offset: %zd, result %zd", codecDataOffset, result);
+ return BAD_VALUE;
+ }
+
+ ALOGV("descramble succeeded, %zd bytes", result);
+
+ if (dstBuffer.type == BufferType::SHARED_MEMORY) {
+ memcpy(it->mCodecBuffer->base(),
+ (uint8_t*)it->mSharedEncryptedBuffer->unsecurePointer(),
+ result);
+ }
+ }
+
+ it->mCodecBuffer->setRange(codecDataOffset, result - codecDataOffset);
+ return OK;
+}
+
status_t ACodecBufferChannel::renderOutputBuffer(
const sp<MediaCodecBuffer> &buffer, int64_t timestampNs) {
std::shared_ptr<const std::vector<const BufferInfo>> array(
@@ -434,6 +608,16 @@
}
void ACodecBufferChannel::setCrypto(const sp<ICrypto> &crypto) {
+ if (mCrypto != nullptr) {
+ for (std::pair<wp<HidlMemory>, int32_t> entry : mHeapSeqNumMap) {
+ mCrypto->unsetHeap(entry.second);
+ }
+ mHeapSeqNumMap.clear();
+ if (mHeapSeqNum >= 0) {
+ mCrypto->unsetHeap(mHeapSeqNum);
+ mHeapSeqNum = -1;
+ }
+ }
mCrypto = crypto;
}
diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp
index 24f6f0b..a1aa5dd 100644
--- a/media/libstagefright/AMRWriter.cpp
+++ b/media/libstagefright/AMRWriter.cpp
@@ -26,7 +26,7 @@
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/mediarecorder.h>
namespace android {
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index e78e1e7..a8fea90 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -41,7 +41,11 @@
cfi: true,
},
- shared_libs: ["libmedia", "libmediandk"],
+ header_libs: [
+ "libstagefright_foundation_headers",
+ ],
+ shared_libs: ["libmediandk"],
+ export_include_dirs: ["include"],
}
cc_library_shared {
@@ -53,6 +57,7 @@
"CodecBase.cpp",
"FrameRenderTracker.cpp",
"MediaCodecListWriter.cpp",
+ "SkipCutBuffer.cpp",
],
cflags: [
@@ -62,6 +67,7 @@
header_libs: [
"libmediadrm_headers",
+ "media_ndk_headers",
],
shared_libs: [
@@ -69,6 +75,7 @@
"libhidlallocatorutils",
"liblog",
"libmedia_codeclist",
+ "libmedia_omx",
"libstagefright_foundation",
"libui",
"libutils",
@@ -96,8 +103,6 @@
shared_libs: [
"liblog",
- "libmedia",
- "libmedia_omx",
],
export_include_dirs: [
@@ -105,7 +110,9 @@
],
header_libs: [
- "libmedia_helper_headers",
+ "libaudioclient_headers",
+ "libmedia_headers",
+ "media_ndk_headers",
],
cflags: [
@@ -212,9 +219,7 @@
"RemoteMediaExtractor.cpp",
"RemoteMediaSource.cpp",
"SimpleDecodingSource.cpp",
- "SkipCutBuffer.cpp",
"StagefrightMediaScanner.cpp",
- "StagefrightPluginLoader.cpp",
"SurfaceUtils.cpp",
"ThrottledSource.cpp",
"Utils.cpp",
@@ -229,6 +234,8 @@
"libbinder",
"libbinder_ndk",
"libcamera_client",
+ "libcodec2",
+ "libcodec2_vndk",
"libcutils",
"libdatasource",
"libdl",
@@ -244,6 +251,7 @@
"libui",
"libutils",
"libmedia_helper",
+ "libsfplugin_ccodec",
"libstagefright_codecbase",
"libstagefright_foundation",
"libstagefright_omx_utils",
diff --git a/media/libstagefright/FrameCaptureProcessor.cpp b/media/libstagefright/FrameCaptureProcessor.cpp
index c517e33..96c1195 100644
--- a/media/libstagefright/FrameCaptureProcessor.cpp
+++ b/media/libstagefright/FrameCaptureProcessor.cpp
@@ -136,7 +136,7 @@
status_t FrameCaptureProcessor::onCapture(const sp<Layer> &layer,
const Rect &sourceCrop, const sp<GraphicBuffer> &buffer) {
renderengine::DisplaySettings clientCompositionDisplay;
- std::vector<renderengine::LayerSettings> clientCompositionLayers;
+ std::vector<const renderengine::LayerSettings*> clientCompositionLayers;
clientCompositionDisplay.physicalDisplay = sourceCrop;
clientCompositionDisplay.clip = sourceCrop;
@@ -150,7 +150,7 @@
layer->getLayerSettings(sourceCrop, mTextureName, &layerSettings);
- clientCompositionLayers.push_back(layerSettings);
+ clientCompositionLayers.push_back(&layerSettings);
// Use an empty fence for the buffer fence, since we just created the buffer so
// there is no need for synchronization with the GPU.
diff --git a/media/libstagefright/MPEG2TSWriter.cpp b/media/libstagefright/MPEG2TSWriter.cpp
index a9715c9..34b840e 100644
--- a/media/libstagefright/MPEG2TSWriter.cpp
+++ b/media/libstagefright/MPEG2TSWriter.cpp
@@ -17,7 +17,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "MPEG2TSWriter"
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/foundation/ABuffer.h>
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index bf4e7de..b29d5f8 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -31,8 +31,9 @@
#include <utils/Log.h>
#include <functional>
+#include <fcntl.h>
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/AUtils.h>
@@ -64,9 +65,6 @@
namespace android {
static const int64_t kMinStreamableFileSizeInBytes = 5 * 1024 * 1024;
-static const int64_t kMax32BitFileSize = 0x00ffffffffLL; // 2^32-1 : max FAT32
- // filesystem file size
- // used by most SD cards
static const uint8_t kNalUnitTypeSeqParamSet = 0x07;
static const uint8_t kNalUnitTypePicParamSet = 0x08;
static const int64_t kInitialDelayTimeUs = 700000LL;
@@ -118,7 +116,7 @@
int64_t getDurationUs() const;
int64_t getEstimatedTrackSizeBytes() const;
int32_t getMetaSizeIncrease(int32_t angle, int32_t trackCount) const;
- void writeTrackHeader(bool use32BitOffset = true);
+ void writeTrackHeader();
int64_t getMinCttsOffsetTimeUs();
void bufferChunk(int64_t timestampUs);
bool isAvc() const { return mIsAvc; }
@@ -136,6 +134,7 @@
static const char *getFourCCForMime(const char *mime);
const char *getTrackType() const;
void resetInternal();
+ int64_t trackMetaDataSize();
private:
// A helper class to handle faster write box with table entries
@@ -295,20 +294,16 @@
int64_t mTrackDurationUs;
int64_t mMaxChunkDurationUs;
int64_t mLastDecodingTimeUs;
-
int64_t mEstimatedTrackSizeBytes;
int64_t mMdatSizeBytes;
int32_t mTimeScale;
pthread_t mThread;
-
List<MediaBuffer *> mChunkSamples;
- bool mSamplesHaveSameSize;
+ bool mSamplesHaveSameSize;
ListTableEntries<uint32_t, 1> *mStszTableEntries;
-
- ListTableEntries<uint32_t, 1> *mStcoTableEntries;
ListTableEntries<off64_t, 1> *mCo64TableEntries;
ListTableEntries<uint32_t, 3> *mStscTableEntries;
ListTableEntries<uint32_t, 1> *mStssTableEntries;
@@ -352,6 +347,8 @@
int64_t mStartTimestampUs;
int64_t mStartTimeRealUs;
int64_t mFirstSampleTimeRealUs;
+ // Captures negative start offset of a track(track starttime < 0).
+ int64_t mFirstSampleStartOffsetUs;
int64_t mPreviousTrackTimeUs;
int64_t mTrackEveryTimeDurationUs;
@@ -361,6 +358,7 @@
ItemRefs mDimgRefs;
Vector<uint16_t> mExifList;
uint16_t mImageItemId;
+ uint16_t mItemIdBase;
int32_t mIsPrimary;
int32_t mWidth, mHeight;
int32_t mTileWidth, mTileHeight;
@@ -410,10 +408,8 @@
void updateTrackSizeEstimate();
void addOneStscTableEntry(size_t chunkId, size_t sampleId);
void addOneStssTableEntry(size_t sampleId);
-
- // Duration is time scale based
- void addOneSttsTableEntry(size_t sampleCount, int32_t timescaledDur);
- void addOneCttsTableEntry(size_t sampleCount, int32_t timescaledDur);
+ void addOneSttsTableEntry(size_t sampleCount, int32_t delta /* media time scale based */);
+ void addOneCttsTableEntry(size_t sampleCount, int32_t sampleOffset);
void addOneElstTableEntry(uint32_t segmentDuration, int32_t mediaTime,
int16_t mediaRate, int16_t mediaRateFraction);
@@ -421,7 +417,7 @@
void sendTrackSummary(bool hasMultipleTracks);
// Write the boxes
- void writeStcoBox(bool use32BitOffset);
+ void writeCo64Box();
void writeStscBox();
void writeStszBox();
void writeStssBox();
@@ -447,7 +443,7 @@
void writeAudioFourCCBox();
void writeVideoFourCCBox();
void writeMetadataFourCCBox();
- void writeStblBox(bool use32BitOffset);
+ void writeStblBox();
void writeEdtsBox();
Track(const Track &);
@@ -489,14 +485,17 @@
mStarted = false;
mWriterThreadStarted = false;
mSendNotify = false;
+ mWriteSeekErr = false;
+ mFallocateErr = false;
// Reset following variables for all the sessions and they will be
// initialized in start(MetaData *param).
mIsRealTimeRecording = true;
mUse4ByteNalLength = true;
- mUse32BitOffset = true;
mOffset = 0;
+ mPreAllocateFileEndOffset = 0;
mMdatOffset = 0;
+ mMdatEndOffset = 0;
mInMemoryCache = NULL;
mInMemoryCacheOffset = 0;
mInMemoryCacheSize = 0;
@@ -505,10 +504,14 @@
mStreamableFile = false;
mTimeScale = -1;
mHasFileLevelMeta = false;
+ mFileLevelMetaDataSize = 0;
mPrimaryItemId = 0;
mAssociationEntryCount = 0;
mNumGrids = 0;
+ mNextItemId = kItemIdBase;
mHasRefs = false;
+ mPreAllocFirstTime = true;
+ mPrevAllTracksTotalMetaDataSizeEstimate = 0;
// Following variables only need to be set for the first recording session.
// And they will stay the same for all the recording sessions.
@@ -530,6 +533,15 @@
ALOGE("cannot seek mFd: %s (%d) %lld", strerror(errno), errno, (long long)mFd);
release();
}
+
+ if (fallocate(mFd, 0, 0, 1) == 0) {
+ ALOGD("PreAllocation enabled");
+ mPreAllocationEnabled = true;
+ } else {
+ ALOGD("PreAllocation disabled. fallocate : %s, %d", strerror(errno), errno);
+ mPreAllocationEnabled = false;
+ }
+
for (List<Track *>::iterator it = mTracks.begin();
it != mTracks.end(); ++it) {
(*it)->resetInternal();
@@ -614,7 +626,8 @@
CHECK(source.get() != NULL);
const char *mime;
- source->getFormat()->findCString(kKeyMIMEType, &mime);
+ sp<MetaData> meta = source->getFormat();
+ meta->findCString(kKeyMIMEType, &mime);
if (Track::getFourCCForMime(mime) == NULL) {
ALOGE("Unsupported mime '%s'", mime);
@@ -736,9 +749,8 @@
// If the estimation is wrong, we will pay the price of wasting
// some reserved space. This should not happen so often statistically.
- static const int32_t factor = mUse32BitOffset? 1: 2;
- static const int64_t MIN_MOOV_BOX_SIZE = 3 * 1024; // 3 KB
- static const int64_t MAX_MOOV_BOX_SIZE = (180 * 3000000 * 6LL / 8000);
+ static const int64_t MIN_MOOV_BOX_SIZE = 3 * 1024; // 3 KibiBytes
+ static const int64_t MAX_MOOV_BOX_SIZE = (180 * 3000000 * 6LL / 8000); // 395.5 KibiBytes
int64_t size = MIN_MOOV_BOX_SIZE;
// Max file size limit is set
@@ -781,10 +793,7 @@
" estimated moov size %" PRId64 " bytes",
mMaxFileSizeLimitBytes, mMaxFileDurationLimitUs, bitRate, size);
- int64_t estimatedSize = factor * size;
- CHECK_GE(estimatedSize, 8);
-
- return estimatedSize;
+ return size;
}
status_t MPEG4Writer::start(MetaData *param) {
@@ -794,36 +803,24 @@
mStartMeta = param;
/*
- * Check mMaxFileSizeLimitBytes at the beginning
- * since mMaxFileSizeLimitBytes may be implicitly
- * changed later for 32-bit file offset even if
- * user does not ask to set it explicitly.
+ * Check mMaxFileSizeLimitBytes at the beginning since mMaxFileSizeLimitBytes may be implicitly
+ * changed later as per filesizebits of filesystem even if user does not set it explicitly.
*/
if (mMaxFileSizeLimitBytes != 0) {
mIsFileSizeLimitExplicitlyRequested = true;
}
- int32_t use64BitOffset;
- if (param &&
- param->findInt32(kKey64BitFileOffset, &use64BitOffset) &&
- use64BitOffset) {
- mUse32BitOffset = false;
- }
-
- if (mUse32BitOffset) {
- // Implicit 32 bit file size limit
- if (mMaxFileSizeLimitBytes == 0) {
- mMaxFileSizeLimitBytes = kMax32BitFileSize;
- }
-
- // If file size is set to be larger than the 32 bit file
- // size limit, treat it as an error.
- if (mMaxFileSizeLimitBytes > kMax32BitFileSize) {
- ALOGW("32-bit file size limit (%" PRId64 " bytes) too big. "
- "It is changed to %" PRId64 " bytes",
- mMaxFileSizeLimitBytes, kMax32BitFileSize);
- mMaxFileSizeLimitBytes = kMax32BitFileSize;
- }
+ int32_t fileSizeBits = fpathconf(mFd, _PC_FILESIZEBITS);
+ ALOGD("fpathconf _PC_FILESIZEBITS:%" PRId32, fileSizeBits);
+ fileSizeBits = std::min(fileSizeBits, 52 /* cap it below 4 peta bytes */);
+ int64_t maxFileSizeBytes = ((int64_t)1 << fileSizeBits) - 1;
+ if (mMaxFileSizeLimitBytes > maxFileSizeBytes) {
+ mMaxFileSizeLimitBytes = maxFileSizeBytes;
+ ALOGD("File size limit (%" PRId64 " bytes) too big. It is changed to %" PRId64 " bytes",
+ mMaxFileSizeLimitBytes, maxFileSizeBytes);
+ } else if (mMaxFileSizeLimitBytes == 0) {
+ mMaxFileSizeLimitBytes = maxFileSizeBytes;
+ ALOGD("File size limit set to %" PRId64 " bytes implicitly", maxFileSizeBytes);
}
int32_t use2ByteNalLength;
@@ -850,7 +847,8 @@
if (!param ||
!param->findInt32(kKeyTimeScale, &mTimeScale)) {
- mTimeScale = 1000;
+ // Increased by a factor of 10 to improve precision of segment duration in edit list entry.
+ mTimeScale = 10000;
}
CHECK_GT(mTimeScale, 0);
ALOGV("movie time scale: %d", mTimeScale);
@@ -915,7 +913,8 @@
if (mInMemoryCacheSize == 0) {
int32_t bitRate = -1;
if (mHasFileLevelMeta) {
- mInMemoryCacheSize += estimateFileLevelMetaSize(param);
+ mFileLevelMetaDataSize = estimateFileLevelMetaSize(param);
+ mInMemoryCacheSize += mFileLevelMetaDataSize;
}
if (mHasMoovBox) {
if (param) {
@@ -926,7 +925,7 @@
}
if (mStreamableFile) {
// Reserve a 'free' box only for streamable file
- lseek64(mFd, mFreeBoxOffset, SEEK_SET);
+ seekOrPostError(mFd, mFreeBoxOffset, SEEK_SET);
writeInt32(mInMemoryCacheSize);
write("free", 4);
mMdatOffset = mFreeBoxOffset + mInMemoryCacheSize;
@@ -935,18 +934,16 @@
}
mOffset = mMdatOffset;
- lseek64(mFd, mMdatOffset, SEEK_SET);
- if (mUse32BitOffset) {
- write("????mdat", 8);
- } else {
- write("\x00\x00\x00\x01mdat????????", 16);
- }
+ seekOrPostError(mFd, mMdatOffset, SEEK_SET);
+ write("\x00\x00\x00\x01mdat????????", 16);
status_t err = startWriterThread();
if (err != OK) {
return err;
}
+ setupAndStartLooper();
+
err = startTracks(param);
if (err != OK) {
return err;
@@ -956,32 +953,30 @@
return OK;
}
-bool MPEG4Writer::use32BitFileOffset() const {
- return mUse32BitOffset;
-}
-
status_t MPEG4Writer::pause() {
ALOGW("MPEG4Writer: pause is not supported");
return ERROR_UNSUPPORTED;
}
-void MPEG4Writer::stopWriterThread() {
- ALOGD("Stopping writer thread");
+status_t MPEG4Writer::stopWriterThread() {
+ ALOGV("Stopping writer thread");
if (!mWriterThreadStarted) {
- return;
+ return OK;
}
-
{
Mutex::Autolock autolock(mLock);
-
mDone = true;
mChunkReadyCondition.signal();
}
void *dummy;
- pthread_join(mThread, &dummy);
+ status_t err = pthread_join(mThread, &dummy);
+ WARN_UNLESS(err == 0, "stopWriterThread pthread_join err: %d", err);
+
+ err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy));
mWriterThreadStarted = false;
- ALOGD("Writer thread stopped");
+ WARN_UNLESS(err == 0, "stopWriterThread pthread_join retVal: %d, writer thread stopped", err);
+ return err;
}
/*
@@ -1037,12 +1032,21 @@
}
void MPEG4Writer::release() {
- close(mFd);
+ ALOGD("release()");
+ if (mPreAllocationEnabled) {
+ truncatePreAllocation();
+ }
+ int retVal = fsync(mFd);
+ WARN_UNLESS(retVal == 0, "fsync retVal:%d", retVal);
+ retVal = close(mFd);
+ WARN_UNLESS(retVal == 0, "close mFd retVal :%d", retVal);
mFd = -1;
if (mNextFd != -1) {
- close(mNextFd);
+ retVal = close(mNextFd);
mNextFd = -1;
+ WARN_UNLESS(retVal == 0, "close mNextFd retVal :%d", retVal);
}
+ stopAndReleaseLooper();
mInitCheck = NO_INIT;
mStarted = false;
free(mInMemoryCache);
@@ -1073,16 +1077,19 @@
}
status_t MPEG4Writer::reset(bool stopSource) {
+ ALOGD("reset()");
+ std::lock_guard<std::mutex> l(mResetMutex);
if (mInitCheck != OK) {
return OK;
} else {
if (!mWriterThreadStarted ||
!mStarted) {
+ status_t err = OK;
if (mWriterThreadStarted) {
- stopWriterThread();
+ err = stopWriterThread();
}
release();
- return OK;
+ return err;
}
}
@@ -1092,9 +1099,10 @@
int32_t nonImageTrackCount = 0;
for (List<Track *>::iterator it = mTracks.begin();
it != mTracks.end(); ++it) {
- status_t status = (*it)->stop(stopSource);
- if (err == OK && status != OK) {
- err = status;
+ status_t trackErr = (*it)->stop(stopSource);
+ if (err == OK && trackErr != OK) {
+ ALOGW("%s track stopped with an error", (*it)->getTrackType());
+ err = trackErr;
}
// skip image tracks
@@ -1110,12 +1118,18 @@
}
}
+
if (nonImageTrackCount > 1) {
ALOGD("Duration from tracks range is [%" PRId64 ", %" PRId64 "] us",
minDurationUs, maxDurationUs);
}
- stopWriterThread();
+ status_t writerErr = stopWriterThread();
+
+ // TODO: which error to propagage, writerErr or trackErr?
+ if (err == OK && writerErr != OK) {
+ err = writerErr;
+ }
// Do not write out movie header on error.
if (err != OK) {
@@ -1124,17 +1138,12 @@
}
// Fix up the size of the 'mdat' chunk.
- if (mUse32BitOffset) {
- lseek64(mFd, mMdatOffset, SEEK_SET);
- uint32_t size = htonl(static_cast<uint32_t>(mOffset - mMdatOffset));
- ::write(mFd, &size, 4);
- } else {
- lseek64(mFd, mMdatOffset + 8, SEEK_SET);
- uint64_t size = mOffset - mMdatOffset;
- size = hton64(size);
- ::write(mFd, &size, 8);
- }
- lseek64(mFd, mOffset, SEEK_SET);
+ seekOrPostError(mFd, mMdatOffset + 8, SEEK_SET);
+ uint64_t size = mOffset - mMdatOffset;
+ size = hton64(size);
+ writeOrPostError(mFd, &size, 8);
+ seekOrPostError(mFd, mOffset, SEEK_SET);
+ mMdatEndOffset = mOffset;
// Construct file-level meta and moov box now
mInMemoryCacheOffset = 0;
@@ -1198,12 +1207,12 @@
CHECK_LE(mInMemoryCacheOffset + 8, mInMemoryCacheSize);
// Cached box
- lseek64(mFd, mFreeBoxOffset, SEEK_SET);
+ seekOrPostError(mFd, mFreeBoxOffset, SEEK_SET);
mOffset = mFreeBoxOffset;
write(mInMemoryCache, 1, mInMemoryCacheOffset);
// Free box
- lseek64(mFd, mOffset, SEEK_SET);
+ seekOrPostError(mFd, mOffset, SEEK_SET);
mFreeBoxOffset = mOffset;
writeInt32(mInMemoryCacheSize - mInMemoryCacheOffset);
write("free", 4);
@@ -1272,20 +1281,19 @@
std::min(minCttsOffsetTimeUs, (*it)->getMinCttsOffsetTimeUs());
}
}
- ALOGI("Ajust the moov start time from %lld us -> %lld us",
- (long long)mStartTimestampUs,
- (long long)(mStartTimestampUs + minCttsOffsetTimeUs - kMaxCttsOffsetTimeUs));
- // Adjust the global start time.
+ ALOGI("Adjust the moov start time from %lld us -> %lld us", (long long)mStartTimestampUs,
+ (long long)(mStartTimestampUs + minCttsOffsetTimeUs - kMaxCttsOffsetTimeUs));
+ // Adjust movie start time.
mStartTimestampUs += minCttsOffsetTimeUs - kMaxCttsOffsetTimeUs;
- // Add mStartTimeOffsetBFramesUs(-ve or zero) to the duration of first entry in STTS.
+ // Add mStartTimeOffsetBFramesUs(-ve or zero) to the start offset of tracks.
mStartTimeOffsetBFramesUs = minCttsOffsetTimeUs - kMaxCttsOffsetTimeUs;
ALOGV("mStartTimeOffsetBFramesUs :%" PRId32, mStartTimeOffsetBFramesUs);
for (List<Track *>::iterator it = mTracks.begin();
it != mTracks.end(); ++it) {
if (!(*it)->isHeic()) {
- (*it)->writeTrackHeader(mUse32BitOffset);
+ (*it)->writeTrackHeader();
}
}
endBox(); // moov
@@ -1376,17 +1384,15 @@
} else {
if (tiffHdrOffset > 0) {
tiffHdrOffset = htonl(tiffHdrOffset);
- ::write(mFd, &tiffHdrOffset, 4); // exif_tiff_header_offset field
+ writeOrPostError(mFd, &tiffHdrOffset, 4); // exif_tiff_header_offset field
mOffset += 4;
}
- ::write(mFd,
- (const uint8_t *)buffer->data() + buffer->range_offset(),
- buffer->range_length());
+ writeOrPostError(mFd, (const uint8_t*)buffer->data() + buffer->range_offset(),
+ buffer->range_length());
mOffset += buffer->range_length();
}
-
*bytesWritten = mOffset - old_offset;
return old_offset;
}
@@ -1431,30 +1437,27 @@
void MPEG4Writer::addLengthPrefixedSample_l(MediaBuffer *buffer) {
size_t length = buffer->range_length();
-
if (mUse4ByteNalLength) {
uint8_t x = length >> 24;
- ::write(mFd, &x, 1);
+ writeOrPostError(mFd, &x, 1);
x = (length >> 16) & 0xff;
- ::write(mFd, &x, 1);
+ writeOrPostError(mFd, &x, 1);
x = (length >> 8) & 0xff;
- ::write(mFd, &x, 1);
+ writeOrPostError(mFd, &x, 1);
x = length & 0xff;
- ::write(mFd, &x, 1);
+ writeOrPostError(mFd, &x, 1);
- ::write(mFd,
- (const uint8_t *)buffer->data() + buffer->range_offset(),
- length);
+ writeOrPostError(mFd, (const uint8_t*)buffer->data() + buffer->range_offset(), length);
mOffset += length + 4;
} else {
CHECK_LT(length, 65536u);
uint8_t x = length >> 8;
- ::write(mFd, &x, 1);
+ writeOrPostError(mFd, &x, 1);
x = length & 0xff;
- ::write(mFd, &x, 1);
- ::write(mFd, (const uint8_t *)buffer->data() + buffer->range_offset(), length);
+ writeOrPostError(mFd, &x, 1);
+ writeOrPostError(mFd, (const uint8_t*)buffer->data() + buffer->range_offset(), length);
mOffset += length + 2;
}
}
@@ -1476,9 +1479,9 @@
it != mBoxes.end(); ++it) {
(*it) += mOffset;
}
- lseek64(mFd, mOffset, SEEK_SET);
- ::write(mFd, mInMemoryCache, mInMemoryCacheOffset);
- ::write(mFd, ptr, bytes);
+ seekOrPostError(mFd, mOffset, SEEK_SET);
+ writeOrPostError(mFd, mInMemoryCache, mInMemoryCacheOffset);
+ writeOrPostError(mFd, ptr, bytes);
mOffset += (bytes + mInMemoryCacheOffset);
// All subsequent boxes will be written to the end of the file.
@@ -1488,13 +1491,54 @@
mInMemoryCacheOffset += bytes;
}
} else {
- ::write(mFd, ptr, size * nmemb);
+ writeOrPostError(mFd, ptr, bytes);
mOffset += bytes;
}
return bytes;
}
+void MPEG4Writer::writeOrPostError(int fd, const void* buf, size_t count) {
+ if (mWriteSeekErr == true)
+ return;
+ ssize_t bytesWritten = ::write(fd, buf, count);
+ /* Write as much as possible during stop() execution when there was an error
+ * (mWriteSeekErr == true) in the previous call to write() or lseek64().
+ */
+ if (bytesWritten == count)
+ return;
+ mWriteSeekErr = true;
+ // Note that errno is not changed even when bytesWritten < count.
+ ALOGE("writeOrPostError bytesWritten:%zd, count:%zu, error:%s(%d)", bytesWritten, count,
+ std::strerror(errno), errno);
+
+ // Can't guarantee that file is usable or write would succeed anymore, hence signal to stop.
+ sp<AMessage> msg = new AMessage(kWhatHandleIOError, mReflector);
+ status_t err = msg->post();
+ ALOGE("writeOrPostError post:%d", err);
+}
+
+void MPEG4Writer::seekOrPostError(int fd, off64_t offset, int whence) {
+ if (mWriteSeekErr == true)
+ return;
+ off64_t resOffset = lseek64(fd, offset, whence);
+ /* Allow to seek during stop() execution even when there was an error
+ * (mWriteSeekErr == true) in the previous call to write() or lseek64().
+ */
+ if (resOffset == offset)
+ return;
+ mWriteSeekErr = true;
+ ALOGE("seekOrPostError resOffset:%" PRIu64 ", offset:%" PRIu64 ", error:%s(%d)", resOffset,
+ offset, std::strerror(errno), errno);
+
+ // Can't guarantee that file is usable or seek would succeed anymore, hence signal to stop.
+ sp<AMessage> msg = new AMessage(kWhatHandleIOError, mReflector);
+ status_t err = msg->post();
+ ALOGE("seekOrPostError post:%d", err);
+}
+
void MPEG4Writer::beginBox(uint32_t id) {
+ ALOGV("beginBox:%" PRIu32, id);
+
mBoxes.push_back(mWriteBoxToMemory?
mInMemoryCacheOffset: mOffset);
@@ -1503,6 +1547,7 @@
}
void MPEG4Writer::beginBox(const char *fourcc) {
+ ALOGV("beginBox:%s", fourcc);
CHECK_EQ(strlen(fourcc), 4u);
mBoxes.push_back(mWriteBoxToMemory?
@@ -1522,10 +1567,11 @@
int32_t x = htonl(mInMemoryCacheOffset - offset);
memcpy(mInMemoryCache + offset, &x, 4);
} else {
- lseek64(mFd, offset, SEEK_SET);
+ seekOrPostError(mFd, offset, SEEK_SET);
writeInt32(mOffset - offset);
+ ALOGV("box size:%" PRIu64, mOffset - offset);
mOffset -= 4;
- lseek64(mFd, mOffset, SEEK_SET);
+ seekOrPostError(mFd, mOffset, SEEK_SET);
}
}
@@ -1679,6 +1725,79 @@
return mStreamableFile;
}
+bool MPEG4Writer::preAllocate(uint64_t wantSize) {
+ if (!mPreAllocationEnabled)
+ return true;
+
+ std::lock_guard<std::mutex> l(mFallocMutex);
+
+ if (mFallocateErr == true)
+ return false;
+
+ // approxMOOVHeadersSize has to be changed whenever its needed in the future.
+ uint64_t approxMOOVHeadersSize = 500;
+ // approxTrackHeadersSize has to be changed whenever its needed in the future.
+ const uint64_t approxTrackHeadersSize = 800;
+
+ uint64_t approxMOOVBoxSize = 0;
+ if (mPreAllocFirstTime) {
+ mPreAllocFirstTime = false;
+ approxMOOVBoxSize = approxMOOVHeadersSize + mFileLevelMetaDataSize + mMoovExtraSize +
+ (approxTrackHeadersSize * numTracks());
+ ALOGV("firstTimeAllocation approxMOOVBoxSize:%" PRIu64, approxMOOVBoxSize);
+ }
+
+ uint64_t allTracksTotalMetaDataSizeEstimate = 0;
+ for (List<Track *>::iterator it = mTracks.begin(); it != mTracks.end(); ++it) {
+ allTracksTotalMetaDataSizeEstimate += ((*it)->trackMetaDataSize());
+ }
+ ALOGV(" allTracksTotalMetaDataSizeEstimate:%" PRIu64, allTracksTotalMetaDataSizeEstimate);
+
+ /* MOOVBoxSize will increase whenever a sample gets written to the file. Enough to allocate
+ * the delta increase for each sample after the very first allocation.
+ */
+ uint64_t approxMetaDataSizeIncrease =
+ allTracksTotalMetaDataSizeEstimate - mPrevAllTracksTotalMetaDataSizeEstimate;
+ ALOGV("approxMetaDataSizeIncrease:%" PRIu64 " wantSize:%" PRIu64, approxMetaDataSizeIncrease,
+ wantSize);
+ mPrevAllTracksTotalMetaDataSizeEstimate = allTracksTotalMetaDataSizeEstimate;
+ ALOGV("mPreAllocateFileEndOffset:%" PRIu64 " mOffset:%" PRIu64, mPreAllocateFileEndOffset,
+ mOffset);
+ off64_t lastFileEndOffset = std::max(mPreAllocateFileEndOffset, mOffset);
+ uint64_t preAllocateSize = wantSize + approxMOOVBoxSize + approxMetaDataSizeIncrease;
+ ALOGV("preAllocateSize :%" PRIu64 " lastFileEndOffset:%" PRIu64, preAllocateSize,
+ lastFileEndOffset);
+
+ int res = fallocate(mFd, 0, lastFileEndOffset, preAllocateSize);
+ if (res == -1) {
+ ALOGE("fallocate err:%s, %d, fd:%d", strerror(errno), errno, mFd);
+ sp<AMessage> msg = new AMessage(kWhatHandleFallocateError, mReflector);
+ status_t err = msg->post();
+ mFallocateErr = true;
+ ALOGD("preAllocation post:%d", err);
+ } else {
+ mPreAllocateFileEndOffset = lastFileEndOffset + preAllocateSize;
+ ALOGV("mPreAllocateFileEndOffset:%" PRIu64, mPreAllocateFileEndOffset);
+ }
+ return (res == -1) ? false : true;
+}
+
+bool MPEG4Writer::truncatePreAllocation() {
+ bool status = true;
+ off64_t endOffset = std::max(mMdatEndOffset, mOffset);
+ /* if mPreAllocateFileEndOffset >= endOffset, then preallocation logic works good. (diff >= 0).
+ * Otherwise, the logic needs to be modified.
+ */
+ ALOGD("ftruncate mPreAllocateFileEndOffset:%" PRId64 " mOffset:%" PRIu64
+ " mMdatEndOffset:%" PRIu64 " diff:%" PRId64, mPreAllocateFileEndOffset, mOffset,
+ mMdatEndOffset, mPreAllocateFileEndOffset - endOffset);
+ if(ftruncate(mFd, endOffset) == -1) {
+ ALOGE("ftruncate err:%s, %d, fd:%d", strerror(errno), errno, mFd);
+ status = false;
+ }
+ return status;
+}
+
bool MPEG4Writer::exceedsFileSizeLimit() {
// No limit
if (mMaxFileSizeLimitBytes == 0) {
@@ -1764,6 +1883,9 @@
return mStartTimestampUs;
}
+/* Returns negative when reordering is needed because of BFrames or zero otherwise.
+ * CTTS values for tracks with BFrames offsets this negative value.
+ */
int32_t MPEG4Writer::getStartTimeOffsetBFramesUs() {
Mutex::Autolock autoLock(mLock);
return mStartTimeOffsetBFramesUs;
@@ -1792,7 +1914,6 @@
mEstimatedTrackSizeBytes(0),
mSamplesHaveSameSize(true),
mStszTableEntries(new ListTableEntries<uint32_t, 1>(1000)),
- mStcoTableEntries(new ListTableEntries<uint32_t, 1>(1000)),
mCo64TableEntries(new ListTableEntries<off64_t, 1>(1000)),
mStscTableEntries(new ListTableEntries<uint32_t, 3>(1000)),
mStssTableEntries(new ListTableEntries<uint32_t, 1>(1000)),
@@ -1807,9 +1928,12 @@
mGotAllCodecSpecificData(false),
mReachedEOS(false),
mStartTimestampUs(-1),
+ mFirstSampleTimeRealUs(0),
+ mFirstSampleStartOffsetUs(0),
mRotation(0),
mDimgRefs("dimg"),
mImageItemId(0),
+ mItemIdBase(0),
mIsPrimary(0),
mWidth(0),
mHeight(0),
@@ -1872,15 +1996,11 @@
mIsMalformed = false;
mTrackDurationUs = 0;
mEstimatedTrackSizeBytes = 0;
- mSamplesHaveSameSize = 0;
+ mSamplesHaveSameSize = false;
if (mStszTableEntries != NULL) {
delete mStszTableEntries;
mStszTableEntries = new ListTableEntries<uint32_t, 1>(1000);
}
- if (mStcoTableEntries != NULL) {
- delete mStcoTableEntries;
- mStcoTableEntries = new ListTableEntries<uint32_t, 1>(1000);
- }
if (mCo64TableEntries != NULL) {
delete mCo64TableEntries;
mCo64TableEntries = new ListTableEntries<off64_t, 1>(1000);
@@ -1908,25 +2028,24 @@
mReachedEOS = false;
}
+int64_t MPEG4Writer::Track::trackMetaDataSize() {
+ int64_t co64BoxSizeBytes = mCo64TableEntries->count() * 8;
+ int64_t stszBoxSizeBytes = mStszTableEntries->count() * 4;
+ int64_t trackMetaDataSize = mStscTableEntries->count() * 12 + // stsc box size
+ mStssTableEntries->count() * 4 + // stss box size
+ mSttsTableEntries->count() * 8 + // stts box size
+ mCttsTableEntries->count() * 8 + // ctts box size
+ mElstTableEntries->count() * 12 + // elst box size
+ co64BoxSizeBytes + // stco box size
+ stszBoxSizeBytes; // stsz box size
+ return trackMetaDataSize;
+}
+
+
void MPEG4Writer::Track::updateTrackSizeEstimate() {
mEstimatedTrackSizeBytes = mMdatSizeBytes; // media data size
-
if (!isHeic() && !mOwner->isFileStreamable()) {
- uint32_t stcoBoxCount = (mOwner->use32BitFileOffset()
- ? mStcoTableEntries->count()
- : mCo64TableEntries->count());
- int64_t stcoBoxSizeBytes = stcoBoxCount * 4;
- int64_t stszBoxSizeBytes = mSamplesHaveSameSize? 4: (mStszTableEntries->count() * 4);
-
- // Reserved free space is not large enough to hold
- // all meta data and thus wasted.
- mEstimatedTrackSizeBytes += mStscTableEntries->count() * 12 + // stsc box size
- mStssTableEntries->count() * 4 + // stss box size
- mSttsTableEntries->count() * 8 + // stts box size
- mCttsTableEntries->count() * 8 + // ctts box size
- mElstTableEntries->count() * 12 + // elst box size
- stcoBoxSizeBytes + // stco box size
- stszBoxSizeBytes; // stsz box size
+ mEstimatedTrackSizeBytes += trackMetaDataSize();
}
}
@@ -1941,24 +2060,20 @@
mStssTableEntries->add(htonl(sampleId));
}
-void MPEG4Writer::Track::addOneSttsTableEntry(
- size_t sampleCount, int32_t duration) {
-
- if (duration == 0) {
+void MPEG4Writer::Track::addOneSttsTableEntry(size_t sampleCount, int32_t delta) {
+ if (delta == 0) {
ALOGW("0-duration samples found: %zu", sampleCount);
}
mSttsTableEntries->add(htonl(sampleCount));
- mSttsTableEntries->add(htonl(duration));
+ mSttsTableEntries->add(htonl(delta));
}
-void MPEG4Writer::Track::addOneCttsTableEntry(
- size_t sampleCount, int32_t duration) {
-
+void MPEG4Writer::Track::addOneCttsTableEntry(size_t sampleCount, int32_t sampleOffset) {
if (!mIsVideo) {
return;
}
mCttsTableEntries->add(htonl(sampleCount));
- mCttsTableEntries->add(htonl(duration));
+ mCttsTableEntries->add(htonl(sampleOffset));
}
void MPEG4Writer::Track::addOneElstTableEntry(
@@ -1971,16 +2086,30 @@
mElstTableEntries->add(htonl((((uint32_t)mediaRate) << 16) | (uint32_t)mediaRateFraction));
}
-status_t MPEG4Writer::setNextFd(int fd) {
- ALOGV("addNextFd");
- Mutex::Autolock l(mLock);
- if (mLooper == NULL) {
- mReflector = new AHandlerReflector<MPEG4Writer>(this);
+void MPEG4Writer::setupAndStartLooper() {
+ if (mLooper == nullptr) {
mLooper = new ALooper;
- mLooper->registerHandler(mReflector);
+ mLooper->setName("MP4WriterLooper");
mLooper->start();
+ mReflector = new AHandlerReflector<MPEG4Writer>(this);
+ mLooper->registerHandler(mReflector);
}
+}
+void MPEG4Writer::stopAndReleaseLooper() {
+ if (mLooper != nullptr) {
+ if (mReflector != nullptr) {
+ ALOGD("unregisterHandler");
+ mLooper->unregisterHandler(mReflector->id());
+ mReflector.clear();
+ }
+ mLooper->stop();
+ mLooper.clear();
+ }
+}
+
+status_t MPEG4Writer::setNextFd(int fd) {
+ Mutex::Autolock l(mLock);
if (mNextFd != -1) {
// No need to set a new FD yet.
return INVALID_OPERATION;
@@ -2021,12 +2150,7 @@
void MPEG4Writer::Track::addChunkOffset(off64_t offset) {
CHECK(!mIsHeic);
- if (mOwner->use32BitFileOffset()) {
- uint32_t value = offset;
- mStcoTableEntries->add(htonl(value));
- } else {
- mCo64TableEntries->add(hton64(offset));
- }
+ mCo64TableEntries->add(hton64(offset));
}
void MPEG4Writer::Track::addItemOffsetAndSize(off64_t offset, size_t size, bool isExif) {
@@ -2042,8 +2166,14 @@
}
if (isExif) {
- mExifList.push_back(mOwner->addItem_l({
+ uint16_t exifItemId;
+ if (mOwner->reserveItemId_l(1, &exifItemId) != OK) {
+ return;
+ }
+
+ mExifList.push_back(mOwner->addItem_l({
.itemType = "Exif",
+ .itemId = exifItemId,
.isPrimary = false,
.isHidden = false,
.offset = (uint32_t)offset,
@@ -2092,6 +2222,7 @@
if (hasGrid) {
mDimgRefs.value.push_back(mOwner->addItem_l({
.itemType = "hvc1",
+ .itemId = mItemIdBase++,
.isPrimary = false,
.isHidden = true,
.offset = (uint32_t)offset,
@@ -2114,6 +2245,7 @@
}
mImageItemId = mOwner->addItem_l({
.itemType = "grid",
+ .itemId = mItemIdBase++,
.isPrimary = (mIsPrimary != 0),
.isHidden = false,
.rows = (uint32_t)mGridRows,
@@ -2126,6 +2258,7 @@
} else {
mImageItemId = mOwner->addItem_l({
.itemType = "hvc1",
+ .itemId = mItemIdBase++,
.isPrimary = (mIsPrimary != 0),
.isHidden = false,
.offset = (uint32_t)offset,
@@ -2195,6 +2328,22 @@
notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED, 0);
break;
}
+ // ::write() or lseek64() wasn't a success, file could be malformed
+ case kWhatHandleIOError: {
+ ALOGE("kWhatHandleIOError");
+ // Stop tracks' threads and main writer thread.
+ notify(MEDIA_RECORDER_EVENT_ERROR, MEDIA_RECORDER_ERROR_UNKNOWN, ERROR_MALFORMED);
+ stop();
+ break;
+ }
+ // fallocate() failed, hence notify app about it and stop().
+ case kWhatHandleFallocateError: {
+ ALOGE("kWhatHandleFallocateError");
+ //TODO: introduce new MEDIA_RECORDER_INFO_STOPPED instead MEDIA_RECORDER_INFO_UNKNOWN?
+ notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_UNKNOWN, ERROR_IO);
+ stop();
+ break;
+ }
default:
TRESPASS();
}
@@ -2236,7 +2385,6 @@
stop();
delete mStszTableEntries;
- delete mStcoTableEntries;
delete mCo64TableEntries;
delete mStscTableEntries;
delete mSttsTableEntries;
@@ -2245,7 +2393,6 @@
delete mElstTableEntries;
mStszTableEntries = NULL;
- mStcoTableEntries = NULL;
mCo64TableEntries = NULL;
mStscTableEntries = NULL;
mSttsTableEntries = NULL;
@@ -2385,7 +2532,6 @@
if (interChunkTimeUs > it->mPrevChunkTimestampUs) {
it->mMaxInterChunkDurUs = interChunkTimeUs;
}
-
return true;
}
}
@@ -2468,6 +2614,22 @@
params->findInt32(kKeyRotation, &rotationDegrees)) {
mRotation = rotationDegrees;
}
+ if (mIsHeic) {
+ // Reserve the item ids, so that the item ids are ordered in the same
+ // order that the image tracks are added.
+ // If we leave the item ids to be assigned when the sample is written out,
+ // the original track order may not be preserved, if two image tracks
+ // have data around the same time. (This could happen especially when
+ // we're encoding with single tile.) The reordering may be undesirable,
+ // even if the file is well-formed and the primary picture is correct.
+
+ // Reserve item ids for samples + grid
+ size_t numItemsToReserve = mNumTiles + (mNumTiles > 1);
+ status_t err = mOwner->reserveItemId_l(numItemsToReserve, &mItemIdBase);
+ if (err != OK) {
+ return err;
+ }
+ }
initTrackingProgressStatus(params);
@@ -2544,10 +2706,11 @@
mDone = true;
void *dummy;
- pthread_join(mThread, &dummy);
- status_t err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy));
-
- ALOGD("%s track stopped. %s source", getTrackType(), stopSource ? "Stop" : "Not Stop");
+ status_t err = pthread_join(mThread, &dummy);
+ WARN_UNLESS(err == 0, "track::stop: pthread_join status:%d", err);
+ err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy));
+ WARN_UNLESS(err == 0, "%s track stopped. Status :%d. %s source", getTrackType(), err,
+ stopSource ? "Stop" : "Not Stop");
return err;
}
@@ -2959,11 +3122,11 @@
uint32_t lastSamplesPerChunk = 0;
if (mIsAudio) {
- prctl(PR_SET_NAME, (unsigned long)"AudioTrackEncoding", 0, 0, 0);
+ prctl(PR_SET_NAME, (unsigned long)"AudioTrackWriterThread", 0, 0, 0);
} else if (mIsVideo) {
- prctl(PR_SET_NAME, (unsigned long)"VideoTrackEncoding", 0, 0, 0);
+ prctl(PR_SET_NAME, (unsigned long)"VideoTrackWriterThread", 0, 0, 0);
} else {
- prctl(PR_SET_NAME, (unsigned long)"MetadataTrackEncoding", 0, 0, 0);
+ prctl(PR_SET_NAME, (unsigned long)"MetadataTrackWriterThread", 0, 0, 0);
}
if (mOwner->isRealTimeRecording()) {
@@ -2983,6 +3146,7 @@
continue;
}
+
// If the codec specific data has not been received yet, delay pause.
// After the codec specific data is received, discard what we received
// when the track is to be paused.
@@ -2993,7 +3157,6 @@
}
++count;
-
int32_t isCodecConfig;
if (buffer->meta_data().findInt32(kKeyIsCodecConfig, &isCodecConfig)
&& isCodecConfig) {
@@ -3060,6 +3223,18 @@
}
}
+ /*
+ * Reserve space in the file for the current sample + to be written MOOV box. If reservation
+ * for a new sample fails, preAllocate(...) stops muxing session completely. Stop() could
+ * write MOOV box successfully as space for the same was reserved in the prior call.
+ * Release the current buffer/sample only here.
+ */
+ if (!mOwner->preAllocate(buffer->range_length())) {
+ buffer->release();
+ buffer = nullptr;
+ break;
+ }
+
++nActualFrames;
// Make a deep copy of the MediaBuffer and Metadata and release
@@ -3071,7 +3246,6 @@
meta_data = new MetaData(buffer->meta_data());
buffer->release();
buffer = NULL;
-
if (isExif) {
copy->meta_data().setInt32(kKeyExifTiffOffset, tiffHdrOffset);
}
@@ -3119,10 +3293,10 @@
if (mOwner->approachingFileSizeLimit()) {
mOwner->notifyApproachingLimit();
}
-
int32_t isSync = false;
meta_data->findInt32(kKeyIsSyncFrame, &isSync);
CHECK(meta_data->findInt64(kKeyTime, ×tampUs));
+ timestampUs += mFirstSampleStartOffsetUs;
// For video, skip the first several non-key frames until getting the first key frame.
if (mIsVideo && !mGotStartKeyFrame && !isSync) {
@@ -3134,10 +3308,13 @@
mGotStartKeyFrame = true;
}
////////////////////////////////////////////////////////////////////////////////
-
if (!mIsHeic) {
if (mStszTableEntries->count() == 0) {
mFirstSampleTimeRealUs = systemTime() / 1000;
+ if (timestampUs < 0 && mFirstSampleStartOffsetUs == 0) {
+ mFirstSampleStartOffsetUs = -timestampUs;
+ timestampUs = 0;
+ }
mOwner->setStartTimestampUs(timestampUs);
mStartTimestampUs = timestampUs;
previousPausedDurationUs = mStartTimestampUs;
@@ -3309,17 +3486,17 @@
}
}
mStszTableEntries->add(htonl(sampleSize));
+
if (mStszTableEntries->count() > 2) {
// Force the first sample to have its own stts entry so that
// we can adjust its value later to maintain the A/V sync.
- if (mStszTableEntries->count() == 3 || currDurationTicks != lastDurationTicks) {
+ if (lastDurationTicks && currDurationTicks != lastDurationTicks) {
addOneSttsTableEntry(sampleCount, lastDurationTicks);
sampleCount = 1;
} else {
++sampleCount;
}
-
}
if (mSamplesHaveSameSize) {
if (mStszTableEntries->count() >= 2 && previousSampleSize != sampleSize) {
@@ -3352,11 +3529,7 @@
if (mIsHeic) {
addItemOffsetAndSize(offset, bytesWritten, isExif);
} else {
- uint32_t count = (mOwner->use32BitFileOffset()
- ? mStcoTableEntries->count()
- : mCo64TableEntries->count());
-
- if (count == 0) {
+ if (mCo64TableEntries->count() == 0) {
addChunkOffset(offset);
}
}
@@ -3392,9 +3565,7 @@
}
}
}
-
}
-
if (isTrackMalFormed()) {
dumpTimeStamps();
err = ERROR_MALFORMED;
@@ -3426,17 +3597,10 @@
++sampleCount; // Count for the last sample
}
- if (mStszTableEntries->count() <= 2) {
- addOneSttsTableEntry(1, lastDurationTicks);
- if (sampleCount - 1 > 0) {
- addOneSttsTableEntry(sampleCount - 1, lastDurationTicks);
- }
- } else {
- addOneSttsTableEntry(sampleCount, lastDurationTicks);
- }
+ addOneSttsTableEntry(sampleCount, lastDurationTicks);
- // The last ctts box may not have been written yet, and this
- // is to make sure that we write out the last ctts box.
+ // The last ctts box entry may not have been written yet, and this
+ // is to make sure that we write out the last ctts box entry.
if (currCttsOffsetTimeTicks == lastCttsOffsetTimeTicks) {
if (cttsSampleCount > 0) {
addOneCttsTableEntry(cttsSampleCount, lastCttsOffsetTimeTicks);
@@ -3687,7 +3851,7 @@
"Metadata";
}
-void MPEG4Writer::Track::writeTrackHeader(bool use32BitOffset) {
+void MPEG4Writer::Track::writeTrackHeader() {
uint32_t now = getMpeg4Time();
mOwner->beginBox("trak");
writeTkhdBox(now);
@@ -3704,7 +3868,7 @@
writeNmhdBox();
}
writeDinfBox();
- writeStblBox(use32BitOffset);
+ writeStblBox();
mOwner->endBox(); // minf
mOwner->endBox(); // mdia
mOwner->endBox(); // trak
@@ -3720,7 +3884,7 @@
return mMinCttsOffsetTimeUs;
}
-void MPEG4Writer::Track::writeStblBox(bool use32BitOffset) {
+void MPEG4Writer::Track::writeStblBox() {
mOwner->beginBox("stbl");
mOwner->beginBox("stsd");
mOwner->writeInt32(0); // version=0, flags=0
@@ -3740,7 +3904,7 @@
}
writeStszBox();
writeStscBox();
- writeStcoBox(use32BitOffset);
+ writeCo64Box();
mOwner->endBox(); // stbl
}
@@ -4099,19 +4263,122 @@
mOwner->endBox();
}
-void MPEG4Writer::Track::writeEdtsBox(){
+void MPEG4Writer::Track::writeEdtsBox() {
ALOGV("%s : getStartTimeOffsetTimeUs of track:%" PRId64 " us", getTrackType(),
getStartTimeOffsetTimeUs());
- // Prepone video playback.
- if (mMinCttsOffsetTicks != mMaxCttsOffsetTicks) {
- int32_t mvhdTimeScale = mOwner->getTimeScale();
- uint32_t tkhdDuration = (getDurationUs() * mvhdTimeScale + 5E5) / 1E6;
- int64_t mediaTime = ((kMaxCttsOffsetTimeUs - getMinCttsOffsetTimeUs())
- * mTimeScale + 5E5) / 1E6;
- if (tkhdDuration > 0 && mediaTime > 0) {
- addOneElstTableEntry(tkhdDuration, mediaTime, 1, 0);
+ int32_t mvhdTimeScale = mOwner->getTimeScale();
+ ALOGV("mvhdTimeScale:%" PRId32, mvhdTimeScale);
+ /* trackStartOffsetUs of this track is the sum of longest offset needed by a track among all
+ * tracks with B frames in this movie and the start offset of this track.
+ */
+ int64_t trackStartOffsetUs = getStartTimeOffsetTimeUs();
+ ALOGV("trackStartOffsetUs:%" PRIu64, trackStartOffsetUs);
+
+ // Longest offset needed by a track among all tracks with B frames.
+ int32_t movieStartOffsetBFramesUs = mOwner->getStartTimeOffsetBFramesUs();
+ ALOGV("movieStartOffsetBFramesUs:%" PRId32, movieStartOffsetBFramesUs);
+
+ // This media/track's real duration (sum of duration of all samples in this track).
+ uint32_t tkhdDurationTicks = (mTrackDurationUs * mvhdTimeScale + 5E5) / 1E6;
+ ALOGV("mTrackDurationUs:%" PRId64 "us", mTrackDurationUs);
+
+ int64_t movieStartTimeUs = mOwner->getStartTimestampUs();
+ ALOGV("movieStartTimeUs:%" PRId64, movieStartTimeUs);
+
+ int64_t trackStartTimeUs = movieStartTimeUs + trackStartOffsetUs;
+ ALOGV("trackStartTimeUs:%" PRId64, trackStartTimeUs);
+
+ if (movieStartOffsetBFramesUs == 0) {
+ // No B frames in any tracks.
+ if (trackStartOffsetUs > 0) {
+ // Track with positive start offset.
+ uint32_t segDuration = (trackStartOffsetUs * mvhdTimeScale + 5E5) / 1E6;
+ ALOGV("segDuration:%" PRIu64 "us", trackStartOffsetUs);
+ /* The first entry is an empty edit (indicated by media_time equal to -1), and its
+ * duration (segment_duration) is equal to the difference of the presentation times of
+ * the earliest media sample among all tracks and the earliest media sample of the track.
+ */
+ ALOGV("Empty edit list entry");
+ addOneElstTableEntry(segDuration, -1, 1, 0);
+ addOneElstTableEntry(tkhdDurationTicks, 0, 1, 0);
+ } else if (mFirstSampleStartOffsetUs > 0) {
+ // Track with start time < 0 / negative start offset.
+ ALOGV("Normal edit list entry");
+ int32_t mediaTime = (mFirstSampleStartOffsetUs * mTimeScale + 5E5) / 1E6;
+ int32_t firstSampleOffsetTicks =
+ (mFirstSampleStartOffsetUs * mvhdTimeScale + 5E5) / 1E6;
+ // samples before 0 don't count in for duration, hence subtract firstSampleOffsetTicks.
+ addOneElstTableEntry(tkhdDurationTicks - firstSampleOffsetTicks, mediaTime, 1, 0);
+ } else {
+ // Track starting at zero.
+ ALOGV("No edit list entry required for this track");
}
+ } else if (movieStartOffsetBFramesUs < 0) {
+ // B frames present in at least one of the tracks.
+ ALOGV("writeEdtsBox - Reordered frames(B frames) present");
+ if (trackStartOffsetUs == std::abs(movieStartOffsetBFramesUs)) {
+ // Track starting at 0, no start offset.
+ // TODO : need to take care of mFirstSampleStartOffsetUs > 0 and trackStartOffsetUs > 0
+ // separately
+ if (mMinCttsOffsetTicks == mMaxCttsOffsetTicks) {
+ // Video with no B frame or non-video track.
+ if (mFirstSampleStartOffsetUs > 0) {
+ // Track with start time < 0 / negative start offset.
+ ALOGV("Normal edit list entry");
+ ALOGV("mFirstSampleStartOffsetUs:%" PRId64 "us", mFirstSampleStartOffsetUs);
+ int32_t mediaTimeTicks = (mFirstSampleStartOffsetUs * mTimeScale + 5E5) / 1E6;
+ int32_t firstSampleOffsetTicks =
+ (mFirstSampleStartOffsetUs * mvhdTimeScale + 5E5) / 1E6;
+ // Samples before 0 don't count for duration, subtract firstSampleOffsetTicks.
+ addOneElstTableEntry(tkhdDurationTicks - firstSampleOffsetTicks, mediaTimeTicks,
+ 1, 0);
+ }
+ } else {
+ // Track with B Frames.
+ int32_t mediaTimeTicks = (trackStartOffsetUs * mTimeScale + 5E5) / 1E6;
+ ALOGV("mediaTime:%" PRId64 "us", trackStartOffsetUs);
+ ALOGV("Normal edit list entry to negate start offset by B Frames in others tracks");
+ addOneElstTableEntry(tkhdDurationTicks, mediaTimeTicks, 1, 0);
+ }
+ } else if (trackStartOffsetUs > std::abs(movieStartOffsetBFramesUs)) {
+ // Track with start offset.
+ ALOGV("Tracks starting > 0");
+ int32_t editDurationTicks = 0;
+ if (mMinCttsOffsetTicks == mMaxCttsOffsetTicks) {
+ // Video with no B frame or non-video track.
+ editDurationTicks =
+ ((trackStartOffsetUs + movieStartOffsetBFramesUs) * mvhdTimeScale + 5E5) /
+ 1E6;
+ ALOGV("editDuration:%" PRId64 "us", (trackStartOffsetUs + movieStartOffsetBFramesUs));
+ } else {
+ // Track with B frame.
+ int32_t trackStartOffsetBFramesUs = getMinCttsOffsetTimeUs() - kMaxCttsOffsetTimeUs;
+ ALOGV("trackStartOffsetBFramesUs:%" PRId32, trackStartOffsetBFramesUs);
+ editDurationTicks =
+ ((trackStartOffsetUs + movieStartOffsetBFramesUs +
+ trackStartOffsetBFramesUs) * mvhdTimeScale + 5E5) / 1E6;
+ ALOGV("editDuration:%" PRId64 "us", (trackStartOffsetUs + movieStartOffsetBFramesUs + trackStartOffsetBFramesUs));
+ }
+ ALOGV("editDurationTicks:%" PRIu32, editDurationTicks);
+ if (editDurationTicks > 0) {
+ ALOGV("Empty edit list entry");
+ addOneElstTableEntry(editDurationTicks, -1, 1, 0);
+ addOneElstTableEntry(tkhdDurationTicks, 0, 1, 0);
+ } else if (editDurationTicks < 0) {
+ // Only video tracks with B Frames would hit this case.
+ ALOGV("Edit list entry to negate start offset by B frames in other tracks");
+ addOneElstTableEntry(tkhdDurationTicks, std::abs(editDurationTicks), 1, 0);
+ } else {
+ ALOGV("No edit list entry needed for this track");
+ }
+ } else {
+ // Not expecting this case as we adjust negative start timestamps to zero.
+ ALOGW("trackStartOffsetUs < std::abs(movieStartOffsetBFramesUs)");
+ }
+ } else {
+ // Neither B frames present nor absent! or any other case?.
+ ALOGW("movieStartOffsetBFramesUs > 0");
}
if (mElstTableEntries->count() == 0) {
@@ -4253,19 +4520,6 @@
void MPEG4Writer::Track::writeSttsBox() {
mOwner->beginBox("stts");
mOwner->writeInt32(0); // version=0, flags=0
- if (mMinCttsOffsetTicks == mMaxCttsOffsetTicks) {
- // For non-vdeio tracks or video tracks without ctts table,
- // adjust duration of first sample for tracks to account for
- // first sample not starting at the media start time.
- // TODO: consider signaling this using some offset
- // as this is not quite correct.
- uint32_t duration;
- CHECK(mSttsTableEntries->get(duration, 1));
- duration = htonl(duration); // Back to host byte order
- int32_t startTimeOffsetScaled = (((getStartTimeOffsetTimeUs() +
- mOwner->getStartTimeOffsetBFramesUs()) * mTimeScale) + 500000LL) / 1000000LL;
- mSttsTableEntries->set(htonl((int32_t)duration + startTimeOffsetScaled), 1);
- }
mSttsTableEntries->write(mOwner);
mOwner->endBox(); // stts
}
@@ -4286,7 +4540,9 @@
mOwner->beginBox("ctts");
mOwner->writeInt32(0); // version=0, flags=0
- int64_t deltaTimeUs = kMaxCttsOffsetTimeUs - getStartTimeOffsetTimeUs();
+ // Adjust ctts entries to have only offset needed for reordering frames.
+ int64_t deltaTimeUs = mMinCttsOffsetTimeUs;
+ ALOGV("ctts deltaTimeUs:%" PRId64, deltaTimeUs);
int64_t delta = (deltaTimeUs * mTimeScale + 500000LL) / 1000000LL;
mCttsTableEntries->adjustEntries([delta](size_t /* ix */, uint32_t (&value)[2]) {
// entries are <count, ctts> pairs; adjust only ctts
@@ -4327,14 +4583,10 @@
mOwner->endBox(); // stsc
}
-void MPEG4Writer::Track::writeStcoBox(bool use32BitOffset) {
- mOwner->beginBox(use32BitOffset? "stco": "co64");
+void MPEG4Writer::Track::writeCo64Box() {
+ mOwner->beginBox("co64");
mOwner->writeInt32(0); // version=0, flags=0
- if (use32BitOffset) {
- mStcoTableEntries->write(mOwner);
- } else {
- mCo64TableEntries->write(mOwner);
- }
+ mCo64TableEntries->write(mOwner);
mOwner->endBox(); // stco or co64
}
@@ -4457,9 +4709,11 @@
}
writeInt16((uint16_t)itemCount);
- for (size_t i = 0; i < itemCount; i++) {
- writeInt16(mItems[i].itemId);
- bool isGrid = mItems[i].isGrid();
+ for (auto it = mItems.begin(); it != mItems.end(); it++) {
+ ItemInfo &item = it->second;
+
+ writeInt16(item.itemId);
+ bool isGrid = item.isGrid();
writeInt16(isGrid ? 1 : 0); // construction_method
writeInt16(0); // data_reference_index = 0
@@ -4470,8 +4724,8 @@
writeInt32(mNumGrids++ * 8);
writeInt32(8);
} else {
- writeInt32(mItems[i].offset);
- writeInt32(mItems[i].size);
+ writeInt32(item.offset);
+ writeInt32(item.size);
}
}
endBox();
@@ -4500,9 +4754,11 @@
}
writeInt16((uint16_t)itemCount);
- for (size_t i = 0; i < itemCount; i++) {
- writeInfeBox(mItems[i].itemId, mItems[i].itemType,
- (mItems[i].isImage() && mItems[i].isHidden) ? 1 : 0);
+ for (auto it = mItems.begin(); it != mItems.end(); it++) {
+ ItemInfo &item = it->second;
+
+ writeInfeBox(item.itemId, item.itemType,
+ (item.isImage() && item.isHidden) ? 1 : 0);
}
endBox();
@@ -4511,20 +4767,22 @@
void MPEG4Writer::writeIdatBox() {
beginBox("idat");
- for (size_t i = 0; i < mItems.size(); i++) {
- if (mItems[i].isGrid()) {
+ for (auto it = mItems.begin(); it != mItems.end(); it++) {
+ ItemInfo &item = it->second;
+
+ if (item.isGrid()) {
writeInt8(0); // version
// flags == 1 means 32-bit width,height
- int8_t flags = (mItems[i].width > 65535 || mItems[i].height > 65535);
+ int8_t flags = (item.width > 65535 || item.height > 65535);
writeInt8(flags);
- writeInt8(mItems[i].rows - 1);
- writeInt8(mItems[i].cols - 1);
+ writeInt8(item.rows - 1);
+ writeInt8(item.cols - 1);
if (flags) {
- writeInt32(mItems[i].width);
- writeInt32(mItems[i].height);
+ writeInt32(item.width);
+ writeInt32(item.height);
} else {
- writeInt16((uint16_t)mItems[i].width);
- writeInt16((uint16_t)mItems[i].height);
+ writeInt16((uint16_t)item.width);
+ writeInt16((uint16_t)item.height);
}
}
}
@@ -4536,11 +4794,13 @@
beginBox("iref");
writeInt32(0); // Version = 0, Flags = 0
{
- for (size_t i = 0; i < mItems.size(); i++) {
- for (size_t r = 0; r < mItems[i].refsList.size(); r++) {
- const ItemRefs &refs = mItems[i].refsList[r];
+ for (auto it = mItems.begin(); it != mItems.end(); it++) {
+ ItemInfo &item = it->second;
+
+ for (size_t r = 0; r < item.refsList.size(); r++) {
+ const ItemRefs &refs = item.refsList[r];
beginBox(refs.key);
- writeInt16(mItems[i].itemId);
+ writeInt16(item.itemId);
size_t refCount = refs.value.size();
if (refCount > 65535) {
ALOGW("too many entries in %s", refs.key);
@@ -4615,12 +4875,14 @@
writeInt32(flags); // Version = 0
writeInt32(mAssociationEntryCount);
- for (size_t itemIndex = 0; itemIndex < mItems.size(); itemIndex++) {
- const Vector<uint16_t> &properties = mItems[itemIndex].properties;
+ for (auto it = mItems.begin(); it != mItems.end(); it++) {
+ ItemInfo &item = it->second;
+
+ const Vector<uint16_t> &properties = item.properties;
if (properties.empty()) {
continue;
}
- writeInt16(mItems[itemIndex].itemId);
+ writeInt16(item.itemId);
size_t entryCount = properties.size();
if (entryCount > 255) {
@@ -4650,19 +4912,21 @@
// patch up the mPrimaryItemId and count items with prop associations
uint16_t firstVisibleItemId = 0;
uint16_t firstImageItemId = 0;
- for (size_t index = 0; index < mItems.size(); index++) {
- if (!mItems[index].isImage()) continue;
+ for (auto it = mItems.begin(); it != mItems.end(); it++) {
+ ItemInfo &item = it->second;
- if (mItems[index].isPrimary) {
- mPrimaryItemId = mItems[index].itemId;
+ if (!item.isImage()) continue;
+
+ if (item.isPrimary) {
+ mPrimaryItemId = item.itemId;
}
if (!firstImageItemId) {
- firstImageItemId = mItems[index].itemId;
+ firstImageItemId = item.itemId;
}
- if (!firstVisibleItemId && !mItems[index].isHidden) {
- firstVisibleItemId = mItems[index].itemId;
+ if (!firstVisibleItemId && !item.isHidden) {
+ firstVisibleItemId = item.itemId;
}
- if (!mItems[index].properties.empty()) {
+ if (!item.properties.empty()) {
mAssociationEntryCount++;
}
}
@@ -4716,15 +4980,25 @@
return mProperties.size();
}
+status_t MPEG4Writer::reserveItemId_l(size_t numItems, uint16_t *itemIdBase) {
+ if (numItems > UINT16_MAX - mNextItemId) {
+ ALOGE("couldn't reserve item ids for %zu items", numItems);
+ return ERROR_OUT_OF_RANGE;
+ }
+ *itemIdBase = mNextItemId;
+ mNextItemId += numItems;
+ return OK;
+}
+
uint16_t MPEG4Writer::addItem_l(const ItemInfo &info) {
ALOGV("addItem_l: type %s, offset %u, size %u",
info.itemType, info.offset, info.size);
- size_t index = mItems.size();
- mItems.push_back(info);
+ if (info.itemId < kItemIdBase || info.itemId >= mNextItemId) {
+ ALOGW("Item id %u is used without reservation!", info.itemId);
+ }
- // make the item id start at kItemIdBase
- mItems.editItemAt(index).itemId = index + kItemIdBase;
+ mItems[info.itemId] = info;
#if (LOG_NDEBUG==0)
if (!info.properties.empty()) {
@@ -4735,24 +5009,28 @@
}
str.append(info.properties[i]);
}
- ALOGV("addItem_l: id %d, properties: %s", mItems[index].itemId, str.c_str());
+ ALOGV("addItem_l: id %d, properties: %s", info.itemId, str.c_str());
}
#endif // (LOG_NDEBUG==0)
- return mItems[index].itemId;
+ return info.itemId;
}
void MPEG4Writer::addRefs_l(uint16_t itemId, const ItemRefs &refs) {
if (refs.value.empty()) {
return;
}
- if (itemId < kItemIdBase) {
- ALOGW("itemId shouldn't be smaller than kItemIdBase");
+ if (itemId < kItemIdBase || itemId >= mNextItemId) {
+ ALOGW("itemId %u for ref is invalid!", itemId);
return;
}
- size_t index = itemId - kItemIdBase;
- mItems.editItemAt(index).refsList.push_back(refs);
+ auto it = mItems.find(itemId);
+ if (it == mItems.end()) {
+ ALOGW("itemId %u was not added yet", itemId);
+ return;
+ }
+ it->second.refsList.push_back(refs);
mHasRefs = true;
}
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 257dd0f..a1b719c 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -21,8 +21,9 @@
#include <inttypes.h>
#include <stdlib.h>
+#include <C2Buffer.h>
+
#include "include/SoftwareRenderer.h"
-#include "StagefrightPluginLoader.h"
#include <android/hardware/cas/native/1.0/IDescrambler.h>
#include <android/hardware/media/omx/1.0/IGraphicBufferSource.h>
@@ -39,7 +40,8 @@
#include <mediadrm/ICrypto.h>
#include <media/IOMX.h>
#include <media/MediaCodecBuffer.h>
-#include <media/MediaAnalyticsItem.h>
+#include <media/MediaCodecInfo.h>
+#include <media/MediaMetricsItem.h>
#include <media/MediaResource.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -51,6 +53,7 @@
#include <media/stagefright/ACodec.h>
#include <media/stagefright/BatteryChecker.h>
#include <media/stagefright/BufferProducerWrapper.h>
+#include <media/stagefright/CCodec.h>
#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/MediaCodecList.h>
#include <media/stagefright/MediaDefs.h>
@@ -101,6 +104,10 @@
static const char *kCodecLatencyHist = "android.media.mediacodec.latency.hist"; /* in us */
static const char *kCodecLatencyUnknown = "android.media.mediacodec.latency.unknown";
+static const char *kCodecNumLowLatencyModeOn = "android.media.mediacodec.low-latency.on"; /* 0..n */
+static const char *kCodecNumLowLatencyModeOff = "android.media.mediacodec.low-latency.off"; /* 0..n */
+static const char *kCodecFirstFrameIndexLowLatencyModeOn = "android.media.mediacodec.low-latency.first-frame"; /* 0..n */
+
// the kCodecRecent* fields appear only in getMetrics() results
static const char *kCodecRecentLatencyMax = "android.media.mediacodec.recent.max"; /* in us */
static const char *kCodecRecentLatencyMin = "android.media.mediacodec.recent.min"; /* in us */
@@ -124,6 +131,9 @@
static const int kMaxReclaimWaitTimeInUs = 500000; // 0.5s
static const int kNumBuffersAlign = 16;
+static const C2MemoryUsage kDefaultReadWriteUsage{
+ C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
+
////////////////////////////////////////////////////////////////////////////////
struct ResourceManagerClient : public BnResourceManagerClient {
@@ -534,9 +544,7 @@
// static
sp<PersistentSurface> MediaCodec::CreatePersistentInputSurface() {
- // allow plugin to create surface
- sp<PersistentSurface> pluginSurface =
- StagefrightPluginLoader::GetCCodecInstance()->createInputSurface();
+ sp<PersistentSurface> pluginSurface = CCodec::CreateInputSurface();
if (pluginSurface != nullptr) {
return pluginSurface;
}
@@ -583,7 +591,12 @@
mHaveInputSurface(false),
mHavePendingInputBuffers(false),
mCpuBoostRequested(false),
- mLatencyUnknown(0) {
+ mLatencyUnknown(0),
+ mNumLowLatencyEnables(0),
+ mNumLowLatencyDisables(0),
+ mIsLowLatencyModeOn(false),
+ mIndexOfFirstFrameWhenLowLatencyOn(-1),
+ mInputBufferCounter(0) {
if (uid == kNoUid) {
mUid = AIBinder_getCallingUid();
} else {
@@ -616,6 +629,16 @@
}
mRecentHead = 0;
}
+
+ {
+ Mutex::Autolock al(mLatencyLock);
+ mBuffersInFlight.clear();
+ mNumLowLatencyEnables = 0;
+ mNumLowLatencyDisables = 0;
+ mIsLowLatencyModeOn = false;
+ mIndexOfFirstFrameWhenLowLatencyOn = -1;
+ mInputBufferCounter = 0;
+ }
}
void MediaCodec::updateMediametrics() {
@@ -641,6 +664,13 @@
mediametrics_setInt64(mMetricsHandle, kCodecLatencyUnknown, mLatencyUnknown);
}
+ {
+ Mutex::Autolock al(mLatencyLock);
+ mediametrics_setInt64(mMetricsHandle, kCodecNumLowLatencyModeOn, mNumLowLatencyEnables);
+ mediametrics_setInt64(mMetricsHandle, kCodecNumLowLatencyModeOff, mNumLowLatencyDisables);
+ mediametrics_setInt64(mMetricsHandle, kCodecFirstFrameIndexLowLatencyModeOn,
+ mIndexOfFirstFrameWhenLowLatencyOn);
+ }
#if 0
// enable for short term, only while debugging
updateEphemeralMediametrics(mMetricsHandle);
@@ -697,6 +727,22 @@
}
}
+void MediaCodec::updateLowLatency(const sp<AMessage> &msg) {
+ int32_t lowLatency = 0;
+ if (msg->findInt32("low-latency", &lowLatency)) {
+ Mutex::Autolock al(mLatencyLock);
+ if (lowLatency > 0) {
+ ++mNumLowLatencyEnables;
+ // This is just an estimate since low latency mode change happens ONLY at key frame
+ mIsLowLatencyModeOn = true;
+ } else if (lowLatency == 0) {
+ ++mNumLowLatencyDisables;
+ // This is just an estimate since low latency mode change happens ONLY at key frame
+ mIsLowLatencyModeOn = false;
+ }
+ }
+}
+
bool MediaCodec::Histogram::setup(int nbuckets, int64_t width, int64_t floor)
{
if (nbuckets <= 0 || width <= 0) {
@@ -813,6 +859,11 @@
// XXX: we *could* make sure that the time is later than the end of queue
// as part of a consistency check...
mBuffersInFlight.push_back(startdata);
+
+ if (mIsLowLatencyModeOn && mIndexOfFirstFrameWhenLowLatencyOn < 0) {
+ mIndexOfFirstFrameWhenLowLatencyOn = mInputBufferCounter;
+ }
+ ++mInputBufferCounter;
}
}
@@ -922,7 +973,7 @@
}
static CodecBase *CreateCCodec() {
- return StagefrightPluginLoader::GetCCodecInstance()->createCodec();
+ return new CCodec;
}
//static
@@ -947,6 +998,28 @@
}
}
+struct CodecListCache {
+ CodecListCache()
+ : mCodecInfoMap{[] {
+ const sp<IMediaCodecList> mcl = MediaCodecList::getInstance();
+ size_t count = mcl->countCodecs();
+ std::map<std::string, sp<MediaCodecInfo>> codecInfoMap;
+ for (size_t i = 0; i < count; ++i) {
+ sp<MediaCodecInfo> info = mcl->getCodecInfo(i);
+ codecInfoMap.emplace(info->getCodecName(), info);
+ }
+ return codecInfoMap;
+ }()} {
+ }
+
+ const std::map<std::string, sp<MediaCodecInfo>> mCodecInfoMap;
+};
+
+static const CodecListCache &GetCodecListCache() {
+ static CodecListCache sCache{};
+ return sCache;
+}
+
status_t MediaCodec::init(const AString &name) {
mResourceManagerProxy->init();
@@ -1133,6 +1206,8 @@
}
}
+ updateLowLatency(format);
+
msg->setMessage("format", format);
msg->setInt32("flags", flags);
msg->setObject("surface", surface);
@@ -1454,6 +1529,75 @@
return err;
}
+status_t MediaCodec::queueBuffer(
+ size_t index,
+ const std::shared_ptr<C2Buffer> &buffer,
+ int64_t presentationTimeUs,
+ uint32_t flags,
+ const sp<AMessage> &tunings,
+ AString *errorDetailMsg) {
+ if (errorDetailMsg != NULL) {
+ errorDetailMsg->clear();
+ }
+
+ sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this);
+ msg->setSize("index", index);
+ sp<WrapperObject<std::shared_ptr<C2Buffer>>> obj{
+ new WrapperObject<std::shared_ptr<C2Buffer>>{buffer}};
+ msg->setObject("c2buffer", obj);
+ msg->setInt64("timeUs", presentationTimeUs);
+ msg->setInt32("flags", flags);
+ msg->setMessage("tunings", tunings);
+ msg->setPointer("errorDetailMsg", errorDetailMsg);
+
+ sp<AMessage> response;
+ status_t err = PostAndAwaitResponse(msg, &response);
+
+ return err;
+}
+
+status_t MediaCodec::queueEncryptedBuffer(
+ size_t index,
+ const sp<hardware::HidlMemory> &buffer,
+ size_t offset,
+ const CryptoPlugin::SubSample *subSamples,
+ size_t numSubSamples,
+ const uint8_t key[16],
+ const uint8_t iv[16],
+ CryptoPlugin::Mode mode,
+ const CryptoPlugin::Pattern &pattern,
+ int64_t presentationTimeUs,
+ uint32_t flags,
+ const sp<AMessage> &tunings,
+ AString *errorDetailMsg) {
+ if (errorDetailMsg != NULL) {
+ errorDetailMsg->clear();
+ }
+
+ sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this);
+ msg->setSize("index", index);
+ sp<WrapperObject<sp<hardware::HidlMemory>>> memory{
+ new WrapperObject<sp<hardware::HidlMemory>>{buffer}};
+ msg->setObject("memory", memory);
+ msg->setSize("offset", offset);
+ msg->setPointer("subSamples", (void *)subSamples);
+ msg->setSize("numSubSamples", numSubSamples);
+ msg->setPointer("key", (void *)key);
+ msg->setPointer("iv", (void *)iv);
+ msg->setInt32("mode", mode);
+ msg->setInt32("encryptBlocks", pattern.mEncryptBlocks);
+ msg->setInt32("skipBlocks", pattern.mSkipBlocks);
+ msg->setInt64("timeUs", presentationTimeUs);
+ msg->setInt32("flags", flags);
+ msg->setMessage("tunings", tunings);
+ msg->setPointer("errorDetailMsg", errorDetailMsg);
+
+ sp<AMessage> response;
+ status_t err = PostAndAwaitResponse(msg, &response);
+
+ return err;
+}
+
status_t MediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
sp<AMessage> msg = new AMessage(kWhatDequeueInputBuffer, this);
msg->setInt64("timeoutUs", timeoutUs);
@@ -2308,6 +2452,23 @@
sp<MediaCodecBuffer> buffer = static_cast<MediaCodecBuffer *>(obj.get());
if (mOutputFormat != buffer->format()) {
+ if (mFlags & kFlagUseBlockModel) {
+ sp<AMessage> diff1 = mOutputFormat->changesFrom(buffer->format());
+ sp<AMessage> diff2 = buffer->format()->changesFrom(mOutputFormat);
+ std::set<std::string> keys;
+ size_t numEntries = diff1->countEntries();
+ AMessage::Type type;
+ for (size_t i = 0; i < numEntries; ++i) {
+ keys.emplace(diff1->getEntryNameAt(i, &type));
+ }
+ numEntries = diff2->countEntries();
+ for (size_t i = 0; i < numEntries; ++i) {
+ keys.emplace(diff2->getEntryNameAt(i, &type));
+ }
+ sp<WrapperObject<std::set<std::string>>> changedKeys{
+ new WrapperObject<std::set<std::string>>{std::move(keys)}};
+ buffer->meta()->setObject("changedKeys", changedKeys);
+ }
mOutputFormat = buffer->format();
ALOGV("[%s] output format changed to: %s",
mComponentName.c_str(), mOutputFormat->debugString(4).c_str());
@@ -2357,7 +2518,7 @@
// format as necessary.
int32_t flags = 0;
(void) buffer->meta()->findInt32("flags", &flags);
- if (flags & BUFFER_FLAG_CODECCONFIG) {
+ if ((flags & BUFFER_FLAG_CODECCONFIG) && !(mFlags & kFlagIsSecure)) {
status_t err =
amendOutputFormatWithCodecSpecificData(buffer);
@@ -2575,6 +2736,15 @@
handleSetSurface(NULL);
}
+ uint32_t flags;
+ CHECK(msg->findInt32("flags", (int32_t *)&flags));
+ if (flags & CONFIGURE_FLAG_USE_BLOCK_MODEL) {
+ if (!(mFlags & kFlagIsAsync)) {
+ PostReplyWithError(replyID, INVALID_OPERATION);
+ break;
+ }
+ mFlags |= kFlagUseBlockModel;
+ }
mReplyID = replyID;
setState(CONFIGURING);
@@ -2600,9 +2770,7 @@
mDescrambler = static_cast<IDescrambler *>(descrambler);
mBufferChannel->setDescrambler(mDescrambler);
- uint32_t flags;
- CHECK(msg->findInt32("flags", (int32_t *)&flags));
-
+ format->setInt32("flags", flags);
if (flags & CONFIGURE_FLAG_ENCODE) {
format->setInt32("encoder", true);
mFlags |= kFlagIsEncoder;
@@ -3187,22 +3355,36 @@
status_t MediaCodec::queueCSDInputBuffer(size_t bufferIndex) {
CHECK(!mCSD.empty());
- const BufferInfo &info = mPortBuffers[kPortIndexInput][bufferIndex];
-
sp<ABuffer> csd = *mCSD.begin();
mCSD.erase(mCSD.begin());
+ std::shared_ptr<C2Buffer> c2Buffer;
- const sp<MediaCodecBuffer> &codecInputData = info.mData;
+ if ((mFlags & kFlagUseBlockModel) && mOwnerName.startsWith("codec2::")) {
+ std::shared_ptr<C2LinearBlock> block =
+ FetchLinearBlock(csd->size(), {std::string{mComponentName.c_str()}});
+ C2WriteView view{block->map().get()};
+ if (view.error() != C2_OK) {
+ return -EINVAL;
+ }
+ if (csd->size() > view.capacity()) {
+ return -EINVAL;
+ }
+ memcpy(view.base(), csd->data(), csd->size());
+ c2Buffer = C2Buffer::CreateLinearBuffer(block->share(0, csd->size(), C2Fence{}));
+ } else {
+ const BufferInfo &info = mPortBuffers[kPortIndexInput][bufferIndex];
+ const sp<MediaCodecBuffer> &codecInputData = info.mData;
- if (csd->size() > codecInputData->capacity()) {
- return -EINVAL;
+ if (csd->size() > codecInputData->capacity()) {
+ return -EINVAL;
+ }
+ if (codecInputData->data() == NULL) {
+ ALOGV("Input buffer %zu is not properly allocated", bufferIndex);
+ return -EINVAL;
+ }
+
+ memcpy(codecInputData->data(), csd->data(), csd->size());
}
- if (codecInputData->data() == NULL) {
- ALOGV("Input buffer %zu is not properly allocated", bufferIndex);
- return -EINVAL;
- }
-
- memcpy(codecInputData->data(), csd->data(), csd->size());
AString errorDetailMsg;
@@ -3213,6 +3395,12 @@
msg->setInt64("timeUs", 0LL);
msg->setInt32("flags", BUFFER_FLAG_CODECCONFIG);
msg->setPointer("errorDetailMsg", &errorDetailMsg);
+ if (c2Buffer) {
+ sp<WrapperObject<std::shared_ptr<C2Buffer>>> obj{
+ new WrapperObject<std::shared_ptr<C2Buffer>>{c2Buffer}};
+ msg->setObject("c2buffer", obj);
+ msg->setMessage("tunings", new AMessage);
+ }
return onQueueInputBuffer(msg);
}
@@ -3318,10 +3506,20 @@
int64_t timeUs;
uint32_t flags;
CHECK(msg->findSize("index", &index));
- CHECK(msg->findSize("offset", &offset));
CHECK(msg->findInt64("timeUs", &timeUs));
CHECK(msg->findInt32("flags", (int32_t *)&flags));
-
+ std::shared_ptr<C2Buffer> c2Buffer;
+ sp<hardware::HidlMemory> memory;
+ sp<RefBase> obj;
+ if (msg->findObject("c2buffer", &obj)) {
+ CHECK(obj);
+ c2Buffer = static_cast<WrapperObject<std::shared_ptr<C2Buffer>> *>(obj.get())->value;
+ } else if (msg->findObject("memory", &obj)) {
+ CHECK(obj);
+ memory = static_cast<WrapperObject<sp<hardware::HidlMemory>> *>(obj.get())->value;
+ } else {
+ CHECK(msg->findSize("offset", &offset));
+ }
const CryptoPlugin::SubSample *subSamples;
size_t numSubSamples;
const uint8_t *key;
@@ -3345,7 +3543,7 @@
pattern.mEncryptBlocks = 0;
pattern.mSkipBlocks = 0;
}
- } else {
+ } else if (!c2Buffer) {
if (!hasCryptoOrDescrambler()) {
ALOGE("[%s] queuing secure buffer without mCrypto or mDescrambler!",
mComponentName.c_str());
@@ -3376,26 +3574,46 @@
}
BufferInfo *info = &mPortBuffers[kPortIndexInput][index];
+ sp<MediaCodecBuffer> buffer = info->mData;
- if (info->mData == nullptr || !info->mOwnedByClient) {
+ if (c2Buffer || memory) {
+ sp<AMessage> tunings;
+ CHECK(msg->findMessage("tunings", &tunings));
+ onSetParameters(tunings);
+
+ status_t err = OK;
+ if (c2Buffer) {
+ err = mBufferChannel->attachBuffer(c2Buffer, buffer);
+ } else if (memory) {
+ err = mBufferChannel->attachEncryptedBuffer(
+ memory, (mFlags & kFlagIsSecure), key, iv, mode, pattern,
+ offset, subSamples, numSubSamples, buffer);
+ }
+ offset = buffer->offset();
+ size = buffer->size();
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ if (buffer == nullptr || !info->mOwnedByClient) {
return -EACCES;
}
- if (offset + size > info->mData->capacity()) {
+ if (offset + size > buffer->capacity()) {
return -EINVAL;
}
- info->mData->setRange(offset, size);
- info->mData->meta()->setInt64("timeUs", timeUs);
+ buffer->setRange(offset, size);
+ buffer->meta()->setInt64("timeUs", timeUs);
if (flags & BUFFER_FLAG_EOS) {
- info->mData->meta()->setInt32("eos", true);
+ buffer->meta()->setInt32("eos", true);
}
if (flags & BUFFER_FLAG_CODECCONFIG) {
- info->mData->meta()->setInt32("csd", true);
+ buffer->meta()->setInt32("csd", true);
}
- sp<MediaCodecBuffer> buffer = info->mData;
status_t err = OK;
if (hasCryptoOrDescrambler()) {
AString *errorDetailMsg;
@@ -3714,6 +3932,7 @@
}
status_t MediaCodec::onSetParameters(const sp<AMessage> ¶ms) {
+ updateLowLatency(params);
mCodec->signalSetParameters(params);
return OK;
@@ -3787,4 +4006,70 @@
return rval;
}
+// static
+status_t MediaCodec::CanFetchLinearBlock(
+ const std::vector<std::string> &names, bool *isCompatible) {
+ *isCompatible = false;
+ if (names.size() == 0) {
+ *isCompatible = true;
+ return OK;
+ }
+ const CodecListCache &cache = GetCodecListCache();
+ for (const std::string &name : names) {
+ auto it = cache.mCodecInfoMap.find(name);
+ if (it == cache.mCodecInfoMap.end()) {
+ return NAME_NOT_FOUND;
+ }
+ const char *owner = it->second->getOwnerName();
+ if (owner == nullptr || strncmp(owner, "default", 8) == 0) {
+ *isCompatible = false;
+ return OK;
+ } else if (strncmp(owner, "codec2::", 8) != 0) {
+ return NAME_NOT_FOUND;
+ }
+ }
+ return CCodec::CanFetchLinearBlock(names, kDefaultReadWriteUsage, isCompatible);
+}
+
+// static
+std::shared_ptr<C2LinearBlock> MediaCodec::FetchLinearBlock(
+ size_t capacity, const std::vector<std::string> &names) {
+ return CCodec::FetchLinearBlock(capacity, kDefaultReadWriteUsage, names);
+}
+
+// static
+status_t MediaCodec::CanFetchGraphicBlock(
+ const std::vector<std::string> &names, bool *isCompatible) {
+ *isCompatible = false;
+ if (names.size() == 0) {
+ *isCompatible = true;
+ return OK;
+ }
+ const CodecListCache &cache = GetCodecListCache();
+ for (const std::string &name : names) {
+ auto it = cache.mCodecInfoMap.find(name);
+ if (it == cache.mCodecInfoMap.end()) {
+ return NAME_NOT_FOUND;
+ }
+ const char *owner = it->second->getOwnerName();
+ if (owner == nullptr || strncmp(owner, "default", 8) == 0) {
+ *isCompatible = false;
+ return OK;
+ } else if (strncmp(owner, "codec2.", 7) != 0) {
+ return NAME_NOT_FOUND;
+ }
+ }
+ return CCodec::CanFetchGraphicBlock(names, isCompatible);
+}
+
+// static
+std::shared_ptr<C2GraphicBlock> MediaCodec::FetchGraphicBlock(
+ int32_t width,
+ int32_t height,
+ int32_t format,
+ uint64_t usage,
+ const std::vector<std::string> &names) {
+ return CCodec::FetchGraphicBlock(width, height, format, usage, names);
+}
+
} // namespace android
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index a267f7e..ac54fa1 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -19,7 +19,6 @@
#include <utils/Log.h>
#include "MediaCodecListOverrides.h"
-#include "StagefrightPluginLoader.h"
#include <binder/IServiceManager.h>
@@ -30,11 +29,14 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/MediaDefs.h>
+#include <media/stagefright/omx/OMXUtils.h>
+#include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
+#include <media/stagefright/CCodec.h>
+#include <media/stagefright/Codec2InfoBuilder.h>
#include <media/stagefright/MediaCodecList.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/OmxInfoBuilder.h>
-#include <media/stagefright/omx/OMXUtils.h>
-#include <xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h>
+#include <media/stagefright/PersistentSurface.h>
#include <sys/stat.h>
#include <utils/threads.h>
@@ -86,8 +88,7 @@
MediaCodecListBuilderBase *GetCodec2InfoBuilder() {
Mutex::Autolock _l(sCodec2InfoBuilderMutex);
if (!sCodec2InfoBuilder) {
- sCodec2InfoBuilder.reset(
- StagefrightPluginLoader::GetCCodecInstance()->createBuilder());
+ sCodec2InfoBuilder.reset(new Codec2InfoBuilder);
}
return sCodec2InfoBuilder.get();
}
@@ -96,8 +97,7 @@
std::vector<MediaCodecListBuilderBase *> builders;
// if plugin provides the input surface, we cannot use OMX video encoders.
// In this case, rely on plugin to provide list of OMX codecs that are usable.
- sp<PersistentSurface> surfaceTest =
- StagefrightPluginLoader::GetCCodecInstance()->createInputSurface();
+ sp<PersistentSurface> surfaceTest = CCodec::CreateInputSurface();
if (surfaceTest == nullptr) {
ALOGD("Allowing all OMX codecs");
builders.push_back(&sOmxInfoBuilder);
diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp
index 7243b82..7fec072 100644
--- a/media/libstagefright/MediaCodecSource.cpp
+++ b/media/libstagefright/MediaCodecSource.cpp
@@ -25,7 +25,7 @@
#include <mediadrm/ICrypto.h>
#include <media/MediaBufferHolder.h>
#include <media/MediaCodecBuffer.h>
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/ALooper.h>
diff --git a/media/libstagefright/MediaMuxer.cpp b/media/libstagefright/MediaMuxer.cpp
index 7ebdb1a..3808d7e 100644
--- a/media/libstagefright/MediaMuxer.cpp
+++ b/media/libstagefright/MediaMuxer.cpp
@@ -24,7 +24,7 @@
#include <media/stagefright/MediaMuxer.h>
#include <media/mediarecorder.h>
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
@@ -48,7 +48,8 @@
MediaMuxer::MediaMuxer(int fd, OutputFormat format)
: mFormat(format),
- mState(UNINITIALIZED) {
+ mState(UNINITIALIZED),
+ mError(OK) {
if (isMp4Format(format)) {
mWriter = new MPEG4Writer(fd);
} else if (format == OUTPUT_FORMAT_WEBM) {
@@ -58,6 +59,7 @@
}
if (mWriter != NULL) {
+ mWriter->setMuxerListener(this);
mFileMeta = new MetaData;
if (format == OUTPUT_FORMAT_HEIF) {
// Note that the key uses recorder file types.
@@ -156,14 +158,22 @@
status_t MediaMuxer::stop() {
Mutex::Autolock autoLock(mMuxerLock);
- if (mState == STARTED) {
+ if (mState == STARTED || mState == ERROR) {
mState = STOPPED;
for (size_t i = 0; i < mTrackList.size(); i++) {
if (mTrackList[i]->stop() != OK) {
return INVALID_OPERATION;
}
}
- return mWriter->stop();
+ status_t err = mWriter->stop();
+ if (err != OK || mError != OK) {
+ ALOGE("stop err: %d, mError:%d", err, mError);
+ }
+ // Prioritize mError over err.
+ if (mError != OK) {
+ err = mError;
+ }
+ return err;
} else {
ALOGE("stop() is called in invalid state %d", mState);
return INVALID_OPERATION;
@@ -212,4 +222,29 @@
return currentTrack->pushBuffer(mediaBuffer);
}
+void MediaMuxer::notify(int msg, int ext1, int ext2) {
+ switch (msg) {
+ case MEDIA_RECORDER_EVENT_ERROR:
+ case MEDIA_RECORDER_TRACK_EVENT_ERROR: {
+ Mutex::Autolock autoLock(mMuxerLock);
+ mState = ERROR;
+ mError = ext2;
+ ALOGW("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
+ break;
+ }
+ case MEDIA_RECORDER_EVENT_INFO: {
+ if (ext1 == MEDIA_RECORDER_INFO_UNKNOWN) {
+ Mutex::Autolock autoLock(mMuxerLock);
+ mState = ERROR;
+ mError = ext2;
+ ALOGW("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
+ }
+ break;
+ }
+ default:
+ // Ignore INFO and other notifications for now.
+ break;
+ }
+}
+
} // namespace android
diff --git a/media/libstagefright/MediaSource.cpp b/media/libstagefright/MediaSource.cpp
index 5bbd3d8..ee0031f 100644
--- a/media/libstagefright/MediaSource.cpp
+++ b/media/libstagefright/MediaSource.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/IMediaSource.h>
namespace android {
diff --git a/media/libstagefright/MetaDataUtils.cpp b/media/libstagefright/MetaDataUtils.cpp
index 3f0bc7d..db60f04 100644
--- a/media/libstagefright/MetaDataUtils.cpp
+++ b/media/libstagefright/MetaDataUtils.cpp
@@ -25,7 +25,6 @@
#include <media/stagefright/foundation/ByteUtils.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaDataUtils.h>
-#include <media/stagefright/Utils.h>
#include <media/NdkMediaFormat.h>
namespace android {
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index 66fb4b0..9fe09fa 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -25,7 +25,7 @@
#include <datasource/DataSourceFactory.h>
#include <datasource/FileSource.h>
#include <media/DataSource.h>
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
diff --git a/media/libstagefright/OggWriter.cpp b/media/libstagefright/OggWriter.cpp
index b738fef..0bc5976 100644
--- a/media/libstagefright/OggWriter.cpp
+++ b/media/libstagefright/OggWriter.cpp
@@ -22,7 +22,7 @@
#include <sys/stat.h>
#include <sys/types.h>
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/mediarecorder.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaDefs.h>
diff --git a/media/libstagefright/RemoteMediaExtractor.cpp b/media/libstagefright/RemoteMediaExtractor.cpp
index 29c3a35..25e43c2 100644
--- a/media/libstagefright/RemoteMediaExtractor.cpp
+++ b/media/libstagefright/RemoteMediaExtractor.cpp
@@ -20,8 +20,8 @@
#include <binder/IPCThreadState.h>
#include <media/stagefright/InterfaceUtils.h>
-#include <media/MediaAnalyticsItem.h>
-#include <media/MediaSource.h>
+#include <media/MediaMetricsItem.h>
+#include <media/stagefright/MediaSource.h>
#include <media/stagefright/RemoteMediaExtractor.h>
// still doing some on/off toggling here.
@@ -48,20 +48,20 @@
mSource(source),
mExtractorPlugin(plugin) {
- mAnalyticsItem = nullptr;
+ mMetricsItem = nullptr;
if (MEDIA_LOG) {
- mAnalyticsItem = MediaAnalyticsItem::create(kKeyExtractor);
+ mMetricsItem = mediametrics::Item::create(kKeyExtractor);
// we're in the extractor service, we want to attribute to the app
// that invoked us.
int uid = IPCThreadState::self()->getCallingUid();
- mAnalyticsItem->setUid(uid);
+ mMetricsItem->setUid(uid);
// track the container format (mpeg, aac, wvm, etc)
size_t ntracks = extractor->countTracks();
- mAnalyticsItem->setCString(kExtractorFormat, extractor->name());
+ mMetricsItem->setCString(kExtractorFormat, extractor->name());
// tracks (size_t)
- mAnalyticsItem->setInt32(kExtractorTracks, ntracks);
+ mMetricsItem->setInt32(kExtractorTracks, ntracks);
// metadata
MetaDataBase pMetaData;
if (extractor->getMetaData(pMetaData) == OK) {
@@ -70,7 +70,7 @@
// 'mime'
const char *mime = nullptr;
if (pMetaData.findCString(kKeyMIMEType, &mime)) {
- mAnalyticsItem->setCString(kExtractorMime, mime);
+ mMetricsItem->setCString(kExtractorMime, mime);
}
// what else is interesting and not already available?
}
@@ -84,15 +84,15 @@
mExtractorPlugin = nullptr;
// log the current record, provided it has some information worth recording
if (MEDIA_LOG) {
- if (mAnalyticsItem != nullptr) {
- if (mAnalyticsItem->count() > 0) {
- mAnalyticsItem->selfrecord();
+ if (mMetricsItem != nullptr) {
+ if (mMetricsItem->count() > 0) {
+ mMetricsItem->selfrecord();
}
}
}
- if (mAnalyticsItem != nullptr) {
- delete mAnalyticsItem;
- mAnalyticsItem = nullptr;
+ if (mMetricsItem != nullptr) {
+ delete mMetricsItem;
+ mMetricsItem = nullptr;
}
}
@@ -123,11 +123,11 @@
}
status_t RemoteMediaExtractor::getMetrics(Parcel *reply) {
- if (mAnalyticsItem == nullptr || reply == nullptr) {
+ if (mMetricsItem == nullptr || reply == nullptr) {
return UNKNOWN_ERROR;
}
- mAnalyticsItem->writeToParcel(reply);
+ mMetricsItem->writeToParcel(reply);
return OK;
}
@@ -139,8 +139,8 @@
return mExtractor->setMediaCas((uint8_t*)casToken.data(), casToken.size());
}
-const char * RemoteMediaExtractor::name() {
- return mExtractor->name();
+String8 RemoteMediaExtractor::name() {
+ return String8(mExtractor->name());
}
////////////////////////////////////////////////////////////////////////////////
diff --git a/media/libstagefright/StagefrightPluginLoader.cpp b/media/libstagefright/StagefrightPluginLoader.cpp
deleted file mode 100644
index fb03c5e..0000000
--- a/media/libstagefright/StagefrightPluginLoader.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright 2018, 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 "StagefrightPluginLoader"
-#include <utils/Log.h>
-
-#include <android-base/properties.h>
-#include <dlfcn.h>
-
-#include "StagefrightPluginLoader.h"
-
-namespace android {
-
-/* static */ Mutex StagefrightPluginLoader::sMutex;
-/* static */ std::unique_ptr<StagefrightPluginLoader> StagefrightPluginLoader::sInstance;
-
-namespace /* unnamed */ {
-
-constexpr const char kCCodecPluginPath[] = "libsfplugin_ccodec.so";
-
-} // unnamed namespace
-
-StagefrightPluginLoader::StagefrightPluginLoader(const char *libPath) {
- if (android::base::GetIntProperty("debug.stagefright.ccodec", 1) == 0) {
- ALOGD("CCodec is disabled.");
- return;
- }
- mLibHandle = dlopen(libPath, RTLD_NOW | RTLD_NODELETE);
- if (mLibHandle == nullptr) {
- ALOGD("Failed to load library: %s (%s)", libPath, dlerror());
- return;
- }
- mCreateCodec = (CodecBase::CreateCodecFunc)dlsym(mLibHandle, "CreateCodec");
- if (mCreateCodec == nullptr) {
- ALOGD("Failed to find symbol: CreateCodec (%s)", dlerror());
- }
- mCreateBuilder = (MediaCodecListBuilderBase::CreateBuilderFunc)dlsym(
- mLibHandle, "CreateBuilder");
- if (mCreateBuilder == nullptr) {
- ALOGD("Failed to find symbol: CreateBuilder (%s)", dlerror());
- }
- mCreateInputSurface = (CodecBase::CreateInputSurfaceFunc)dlsym(
- mLibHandle, "CreateInputSurface");
- if (mCreateInputSurface == nullptr) {
- ALOGD("Failed to find symbol: CreateInputSurface (%s)", dlerror());
- }
-}
-
-StagefrightPluginLoader::~StagefrightPluginLoader() {
- if (mLibHandle != nullptr) {
- ALOGV("Closing handle");
- dlclose(mLibHandle);
- }
-}
-
-CodecBase *StagefrightPluginLoader::createCodec() {
- if (mLibHandle == nullptr || mCreateCodec == nullptr) {
- ALOGD("Handle or CreateCodec symbol is null");
- return nullptr;
- }
- return mCreateCodec();
-}
-
-MediaCodecListBuilderBase *StagefrightPluginLoader::createBuilder() {
- if (mLibHandle == nullptr || mCreateBuilder == nullptr) {
- ALOGD("Handle or CreateBuilder symbol is null");
- return nullptr;
- }
- return mCreateBuilder();
-}
-
-PersistentSurface *StagefrightPluginLoader::createInputSurface() {
- if (mLibHandle == nullptr || mCreateInputSurface == nullptr) {
- ALOGD("Handle or CreateInputSurface symbol is null");
- return nullptr;
- }
- return mCreateInputSurface();
-}
-
-//static
-const std::unique_ptr<StagefrightPluginLoader> &StagefrightPluginLoader::GetCCodecInstance() {
- Mutex::Autolock _l(sMutex);
- if (!sInstance) {
- ALOGV("Loading library");
- sInstance.reset(new StagefrightPluginLoader(kCCodecPluginPath));
- }
- return sInstance;
-}
-
-} // namespace android
diff --git a/media/libstagefright/StagefrightPluginLoader.h b/media/libstagefright/StagefrightPluginLoader.h
deleted file mode 100644
index 78effbf..0000000
--- a/media/libstagefright/StagefrightPluginLoader.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef STAGEFRIGHT_PLUGIN_LOADER_H_
-
-#define STAGEFRIGHT_PLUGIN_LOADER_H_
-
-#include <media/stagefright/CodecBase.h>
-#include <media/stagefright/MediaCodecListWriter.h>
-#include <media/stagefright/PersistentSurface.h>
-#include <utils/Mutex.h>
-
-namespace android {
-
-class StagefrightPluginLoader {
-public:
- static const std::unique_ptr<StagefrightPluginLoader> &GetCCodecInstance();
- ~StagefrightPluginLoader();
-
- CodecBase *createCodec();
- MediaCodecListBuilderBase *createBuilder();
- PersistentSurface *createInputSurface();
-
-private:
- explicit StagefrightPluginLoader(const char *libPath);
-
- static Mutex sMutex;
- static std::unique_ptr<StagefrightPluginLoader> sInstance;
-
- void *mLibHandle{nullptr};
- CodecBase::CreateCodecFunc mCreateCodec{nullptr};
- MediaCodecListBuilderBase::CreateBuilderFunc mCreateBuilder{nullptr};
- CodecBase::CreateInputSurfaceFunc mCreateInputSurface{nullptr};
-};
-
-} // namespace android
-
-#endif // STAGEFRIGHT_PLUGIN_LOADER_H_
diff --git a/media/libstagefright/codecs/amrnb/common/Android.bp b/media/libstagefright/codecs/amrnb/common/Android.bp
index 772ebf9..ea8b073 100644
--- a/media/libstagefright/codecs/amrnb/common/Android.bp
+++ b/media/libstagefright/codecs/amrnb/common/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
name: "libstagefright_amrnb_common",
vendor_available: true,
diff --git a/media/libstagefright/codecs/amrnb/dec/test/AmrnbDecTestEnvironment.h b/media/libstagefright/codecs/amrnb/dec/test/AmrnbDecTestEnvironment.h
new file mode 100644
index 0000000..0344ac5
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/dec/test/AmrnbDecTestEnvironment.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __AMRNBDEC_TEST_ENVIRONMENT_H__
+#define __AMRNBDEC_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class AmrnbDecTestEnvironment : public ::testing::Environment {
+ public:
+ AmrnbDecTestEnvironment() : res("/data/local/tmp/") {}
+
+ // Parses the command line arguments
+ int initFromOptions(int argc, char **argv);
+
+ void setRes(const char *_res) { res = _res; }
+
+ const string getRes() const { return res; }
+
+ private:
+ string res;
+};
+
+int AmrnbDecTestEnvironment::initFromOptions(int argc, char **argv) {
+ static struct option options[] = {{"res", required_argument, 0, 'P'}, {0, 0, 0, 0}};
+
+ while (true) {
+ int index = 0;
+ int c = getopt_long(argc, argv, "P:", options, &index);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'P':
+ setRes(optarg);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr,
+ "unrecognized option: %s\n\n"
+ "usage: %s <gtest options> <test options>\n\n"
+ "test options are:\n\n"
+ "-P, --path: Resource files directory location\n",
+ argv[optind ?: 1], argv[0]);
+ return 2;
+ }
+ return 0;
+}
+
+#endif // __AMRNBDEC_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/codecs/amrnb/dec/test/AmrnbDecoderTest.cpp b/media/libstagefright/codecs/amrnb/dec/test/AmrnbDecoderTest.cpp
new file mode 100644
index 0000000..af62074
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/dec/test/AmrnbDecoderTest.cpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2019 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 "AmrnbDecoderTest"
+#define OUTPUT_FILE "/data/local/tmp/amrnbDecode.out"
+
+#include <utils/Log.h>
+
+#include <audio_utils/sndfile.h>
+#include <stdio.h>
+
+#include "gsmamr_dec.h"
+
+#include "AmrnbDecTestEnvironment.h"
+
+// Constants for AMR-NB
+constexpr int32_t kInputBufferSize = 64;
+constexpr int32_t kSamplesPerFrame = L_FRAME;
+constexpr int32_t kBitsPerSample = 16;
+constexpr int32_t kSampleRate = 8000;
+constexpr int32_t kChannels = 1;
+constexpr int32_t kOutputBufferSize = kSamplesPerFrame * kBitsPerSample / 8;
+const int32_t kFrameSizes[] = {12, 13, 15, 17, 19, 20, 26, 31, -1, -1, -1, -1, -1, -1, -1, -1};
+
+constexpr int32_t kNumFrameReset = 150;
+
+static AmrnbDecTestEnvironment *gEnv = nullptr;
+
+class AmrnbDecoderTest : public ::testing::TestWithParam<string> {
+ public:
+ AmrnbDecoderTest() : mFpInput(nullptr) {}
+
+ ~AmrnbDecoderTest() {
+ if (mFpInput) {
+ fclose(mFpInput);
+ mFpInput = nullptr;
+ }
+ }
+
+ FILE *mFpInput;
+ SNDFILE *openOutputFile(SF_INFO *sfInfo);
+ int32_t DecodeFrames(void *amrHandle, SNDFILE *outFileHandle, int32_t frameCount = INT32_MAX);
+};
+
+SNDFILE *AmrnbDecoderTest::openOutputFile(SF_INFO *sfInfo) {
+ memset(sfInfo, 0, sizeof(SF_INFO));
+ sfInfo->channels = kChannels;
+ sfInfo->format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
+ sfInfo->samplerate = kSampleRate;
+ SNDFILE *outFileHandle = sf_open(OUTPUT_FILE, SFM_WRITE, sfInfo);
+ return outFileHandle;
+}
+
+int32_t AmrnbDecoderTest::DecodeFrames(void *amrHandle, SNDFILE *outFileHandle,
+ int32_t frameCount) {
+ uint8_t inputBuf[kInputBufferSize];
+ int16_t outputBuf[kOutputBufferSize];
+
+ while (frameCount > 0) {
+ uint8_t mode;
+ int32_t bytesRead = fread(&mode, 1, 1, mFpInput);
+ if (bytesRead != 1) break;
+
+ // Find frame type
+ Frame_Type_3GPP frameType = (Frame_Type_3GPP)((mode >> 3) & 0x0f);
+ int32_t frameSize = kFrameSizes[frameType];
+ if (frameSize < 0) {
+ ALOGE("Illegal frame type");
+ return -1;
+ }
+ bytesRead = fread(inputBuf, 1, frameSize, mFpInput);
+ if (bytesRead != frameSize) break;
+
+ int32_t bytesDecoded = AMRDecode(amrHandle, frameType, inputBuf, outputBuf, MIME_IETF);
+ if (bytesDecoded == -1) {
+ ALOGE("Failed to decode the input file");
+ return -1;
+ }
+
+ sf_writef_short(outFileHandle, outputBuf, kSamplesPerFrame);
+ frameCount--;
+ }
+ return 0;
+}
+
+TEST_F(AmrnbDecoderTest, CreateAmrnbDecoderTest) {
+ void *amrHandle;
+ int32_t status = GSMInitDecode(&amrHandle, (Word8 *)"AMRNBDecoder");
+ ASSERT_EQ(status, 0) << "Error creating AMR-NB decoder";
+ GSMDecodeFrameExit(&amrHandle);
+ ASSERT_EQ(amrHandle, nullptr) << "Error deleting AMR-NB decoder";
+}
+
+TEST_P(AmrnbDecoderTest, DecodeTest) {
+ string inputFile = gEnv->getRes() + GetParam();
+ mFpInput = fopen(inputFile.c_str(), "rb");
+ ASSERT_NE(mFpInput, nullptr) << "Error opening input file " << inputFile;
+
+ // Open the output file.
+ SF_INFO sfInfo;
+ SNDFILE *outFileHandle = openOutputFile(&sfInfo);
+ ASSERT_NE(outFileHandle, nullptr) << "Error opening output file for writing decoded output";
+
+ void *amrHandle;
+ int32_t status = GSMInitDecode(&amrHandle, (Word8 *)"AMRNBDecoder");
+ ASSERT_EQ(status, 0) << "Error creating AMR-NB decoder";
+
+ // Decode
+ int32_t decoderErr = DecodeFrames(amrHandle, outFileHandle);
+ ASSERT_EQ(decoderErr, 0) << "DecodeFrames returned error";
+
+ sf_close(outFileHandle);
+ GSMDecodeFrameExit(&amrHandle);
+ ASSERT_EQ(amrHandle, nullptr) << "Error deleting AMR-NB decoder";
+}
+
+TEST_P(AmrnbDecoderTest, ResetDecodeTest) {
+ string inputFile = gEnv->getRes() + GetParam();
+ mFpInput = fopen(inputFile.c_str(), "rb");
+ ASSERT_NE(mFpInput, nullptr) << "Error opening input file " << inputFile;
+
+ // Open the output file.
+ SF_INFO sfInfo;
+ SNDFILE *outFileHandle = openOutputFile(&sfInfo);
+ ASSERT_NE(outFileHandle, nullptr) << "Error opening output file for writing decoded output";
+
+ void *amrHandle;
+ int32_t status = GSMInitDecode(&amrHandle, (Word8 *)"AMRNBDecoder");
+ ASSERT_EQ(status, 0) << "Error creating AMR-NB decoder";
+
+ // Decode kNumFrameReset first
+ int32_t decoderErr = DecodeFrames(amrHandle, outFileHandle, kNumFrameReset);
+ ASSERT_EQ(decoderErr, 0) << "DecodeFrames returned error";
+
+ status = Speech_Decode_Frame_reset(amrHandle);
+ ASSERT_EQ(status, 0) << "Error resting AMR-NB decoder";
+
+ // Start decoding again
+ decoderErr = DecodeFrames(amrHandle, outFileHandle);
+ ASSERT_EQ(decoderErr, 0) << "DecodeFrames returned error";
+
+ sf_close(outFileHandle);
+ GSMDecodeFrameExit(&amrHandle);
+ ASSERT_EQ(amrHandle, nullptr) << "Error deleting AMR-NB decoder";
+}
+
+INSTANTIATE_TEST_SUITE_P(AmrnbDecoderTestAll, AmrnbDecoderTest,
+ ::testing::Values(("bbb_8000hz_1ch_8kbps_amrnb_30sec.amrnb"),
+ ("sine_amrnb_1ch_12kbps_8000hz.amrnb")));
+
+int main(int argc, char **argv) {
+ gEnv = new AmrnbDecTestEnvironment();
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = gEnv->initFromOptions(argc, argv);
+ if (status == 0) {
+ status = RUN_ALL_TESTS();
+ ALOGV("Test result = %d\n", status);
+ }
+ return status;
+}
diff --git a/media/libstagefright/codecs/amrnb/dec/test/Android.bp b/media/libstagefright/codecs/amrnb/dec/test/Android.bp
new file mode 100644
index 0000000..7a95cfa
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/dec/test/Android.bp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+cc_test {
+ name: "AmrnbDecoderTest",
+ gtest: true,
+
+ srcs: [
+ "AmrnbDecoderTest.cpp",
+ ],
+
+ static_libs: [
+ "libstagefright_amrnb_common",
+ "libstagefright_amrnbdec",
+ "libaudioutils",
+ "libsndfile",
+ ],
+
+ shared_libs: [
+ "liblog",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ sanitize: {
+ cfi: true,
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ },
+}
diff --git a/media/libstagefright/codecs/amrnb/dec/test/README.md b/media/libstagefright/codecs/amrnb/dec/test/README.md
new file mode 100644
index 0000000..62e13ae
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/dec/test/README.md
@@ -0,0 +1,34 @@
+## Media Testing ##
+---
+#### AMR-NB Decoder :
+The Amr-Nb Decoder Test Suite validates the amrnb decoder available in libstagefright.
+
+Run the following steps to build the test suite:
+```
+m AmrnbDecoderTest
+```
+
+The 32-bit binaries will be created in the following path : ${OUT}/data/nativetest/
+
+The 64-bit binaries will be created in the following path : ${OUT}/data/nativetest64/
+
+To test 64-bit binary push binaries from nativetest64.
+```
+adb push ${OUT}/data/nativetest64/AmrnbDecoderTest/AmrnbDecoderTest /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/AmrnbDecoderTest/AmrnbDecoderTest /data/local/tmp/
+```
+
+The resource file for the tests is taken from [here](https://drive.google.com/drive/folders/13cM4tAaVFrmr-zGFqaAzFBbKs75pnm9b). Push these files into device for testing.
+Download amr-nb folder and push all the files in this folder to /data/local/tmp/ on the device.
+```
+adb push amr-nb/. /data/local/tmp/
+```
+
+usage: AmrnbDecoderTest -P \<path_to_folder\>
+```
+adb shell /data/local/tmp/AmrnbDecoderTest -P /data/local/tmp/
+```
diff --git a/media/libstagefright/codecs/amrnb/enc/test/AmrnbEncTestEnvironment.h b/media/libstagefright/codecs/amrnb/enc/test/AmrnbEncTestEnvironment.h
new file mode 100644
index 0000000..5a2fcd1
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/enc/test/AmrnbEncTestEnvironment.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __AMRNBENC_TEST_ENVIRONMENT_H__
+#define __AMRNBENC_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class AmrnbEncTestEnvironment : public ::testing::Environment {
+ public:
+ AmrnbEncTestEnvironment() : res("/data/local/tmp/") {}
+
+ // Parses the command line arguments
+ int initFromOptions(int argc, char **argv);
+
+ void setRes(const char *_res) { res = _res; }
+
+ const string getRes() const { return res; }
+
+ private:
+ string res;
+};
+
+int AmrnbEncTestEnvironment::initFromOptions(int argc, char **argv) {
+ static struct option options[] = {{"res", required_argument, 0, 'P'}, {0, 0, 0, 0}};
+
+ while (true) {
+ int index = 0;
+ int c = getopt_long(argc, argv, "P:", options, &index);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'P':
+ setRes(optarg);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr,
+ "unrecognized option: %s\n\n"
+ "usage: %s <gtest options> <test options>\n\n"
+ "test options are:\n\n"
+ "-P, --path: Resource files directory location\n",
+ argv[optind ?: 1], argv[0]);
+ return 2;
+ }
+ return 0;
+}
+
+#endif // __AMRNBENC_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/codecs/amrnb/enc/test/AmrnbEncoderTest.cpp b/media/libstagefright/codecs/amrnb/enc/test/AmrnbEncoderTest.cpp
new file mode 100644
index 0000000..fb72998
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/enc/test/AmrnbEncoderTest.cpp
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2019 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 "AmrnbEncoderTest"
+
+#include <utils/Log.h>
+
+#include <audio_utils/sndfile.h>
+#include <stdio.h>
+
+#include "gsmamr_enc.h"
+
+#include "AmrnbEncTestEnvironment.h"
+
+#define OUTPUT_FILE "/data/local/tmp/amrnbEncode.out"
+
+constexpr int32_t kInputBufferSize = L_FRAME * 2; // 160 samples * 16-bit per sample.
+constexpr int32_t kOutputBufferSize = 1024;
+constexpr int32_t kNumFrameReset = 200;
+constexpr int32_t kMaxCount = 10;
+struct AmrNbEncState {
+ void *encCtx;
+ void *pidSyncCtx;
+};
+
+static AmrnbEncTestEnvironment *gEnv = nullptr;
+
+class AmrnbEncoderTest : public ::testing::TestWithParam<pair<string, int32_t>> {
+ public:
+ AmrnbEncoderTest() : mAmrEncHandle(nullptr) {}
+
+ ~AmrnbEncoderTest() {
+ if (mAmrEncHandle) {
+ free(mAmrEncHandle);
+ mAmrEncHandle = nullptr;
+ }
+ }
+
+ AmrNbEncState *mAmrEncHandle;
+ int32_t EncodeFrames(int32_t mode, FILE *fpInput, FILE *mFpOutput,
+ int32_t frameCount = INT32_MAX);
+};
+
+int32_t AmrnbEncoderTest::EncodeFrames(int32_t mode, FILE *fpInput, FILE *mFpOutput,
+ int32_t frameCount) {
+ int32_t frameNum = 0;
+ uint16_t inputBuf[kInputBufferSize];
+ uint8_t outputBuf[kOutputBufferSize];
+ while (frameNum < frameCount) {
+ int32_t bytesRead = fread(inputBuf, 1, kInputBufferSize, fpInput);
+ if (bytesRead != kInputBufferSize && !feof(fpInput)) {
+ ALOGE("Unable to read data from input file");
+ return -1;
+ } else if (feof(fpInput) && bytesRead == 0) {
+ break;
+ }
+ Frame_Type_3GPP frame_type = (Frame_Type_3GPP)mode;
+ int32_t bytesGenerated =
+ AMREncode(mAmrEncHandle->encCtx, mAmrEncHandle->pidSyncCtx, (Mode)mode,
+ (Word16 *)inputBuf, outputBuf, &frame_type, AMR_TX_WMF);
+ frameNum++;
+ if (bytesGenerated < 0) {
+ ALOGE("Error in encoging the file: Invalid output format");
+ return -1;
+ }
+
+ // Convert from WMF to RFC 3267 format.
+ if (bytesGenerated > 0) {
+ outputBuf[0] = ((outputBuf[0] << 3) | 4) & 0x7c;
+ }
+ fwrite(outputBuf, 1, bytesGenerated, mFpOutput);
+ }
+ return 0;
+}
+
+TEST_F(AmrnbEncoderTest, CreateAmrnbEncoderTest) {
+ mAmrEncHandle = (AmrNbEncState *)malloc(sizeof(AmrNbEncState));
+ ASSERT_NE(mAmrEncHandle, nullptr) << "Error in allocating memory to Codec handle";
+ for (int count = 0; count < kMaxCount; count++) {
+ int32_t status = AMREncodeInit(&mAmrEncHandle->encCtx, &mAmrEncHandle->pidSyncCtx, 0);
+ ASSERT_EQ(status, 0) << "Error creating AMR-NB encoder";
+ ALOGV("Successfully created encoder");
+ }
+ if (mAmrEncHandle) {
+ AMREncodeExit(&mAmrEncHandle->encCtx, &mAmrEncHandle->pidSyncCtx);
+ ASSERT_EQ(mAmrEncHandle->encCtx, nullptr) << "Error deleting AMR-NB encoder";
+ ASSERT_EQ(mAmrEncHandle->pidSyncCtx, nullptr) << "Error deleting AMR-NB encoder";
+ free(mAmrEncHandle);
+ mAmrEncHandle = nullptr;
+ ALOGV("Successfully deleted encoder");
+ }
+}
+
+TEST_P(AmrnbEncoderTest, EncodeTest) {
+ mAmrEncHandle = (AmrNbEncState *)malloc(sizeof(AmrNbEncState));
+ ASSERT_NE(mAmrEncHandle, nullptr) << "Error in allocating memory to Codec handle";
+ int32_t status = AMREncodeInit(&mAmrEncHandle->encCtx, &mAmrEncHandle->pidSyncCtx, 0);
+ ASSERT_EQ(status, 0) << "Error creating AMR-NB encoder";
+
+ string inputFile = gEnv->getRes() + GetParam().first;
+ FILE *fpInput = fopen(inputFile.c_str(), "rb");
+ ASSERT_NE(fpInput, nullptr) << "Error opening input file " << inputFile;
+
+ FILE *fpOutput = fopen(OUTPUT_FILE, "wb");
+ ASSERT_NE(fpOutput, nullptr) << "Error opening output file " << OUTPUT_FILE;
+
+ // Write file header.
+ fwrite("#!AMR\n", 1, 6, fpOutput);
+
+ int32_t mode = GetParam().second;
+ int32_t encodeErr = EncodeFrames(mode, fpInput, fpOutput);
+ ASSERT_EQ(encodeErr, 0) << "EncodeFrames returned error for Codec mode: " << mode;
+
+ fclose(fpOutput);
+ fclose(fpInput);
+
+ AMREncodeExit(&mAmrEncHandle->encCtx, &mAmrEncHandle->pidSyncCtx);
+ ASSERT_EQ(mAmrEncHandle->encCtx, nullptr) << "Error deleting AMR-NB encoder";
+ ASSERT_EQ(mAmrEncHandle->pidSyncCtx, nullptr) << "Error deleting AMR-NB encoder";
+ free(mAmrEncHandle);
+ mAmrEncHandle = nullptr;
+ ALOGV("Successfully deleted encoder");
+}
+
+TEST_P(AmrnbEncoderTest, ResetEncoderTest) {
+ mAmrEncHandle = (AmrNbEncState *)malloc(sizeof(AmrNbEncState));
+ ASSERT_NE(mAmrEncHandle, nullptr) << "Error in allocating memory to Codec handle";
+ int32_t status = AMREncodeInit(&mAmrEncHandle->encCtx, &mAmrEncHandle->pidSyncCtx, 0);
+ ASSERT_EQ(status, 0) << "Error creating AMR-NB encoder";
+
+ string inputFile = gEnv->getRes() + GetParam().first;
+ FILE *fpInput = fopen(inputFile.c_str(), "rb");
+ ASSERT_NE(fpInput, nullptr) << "Error opening input file " << inputFile;
+
+ FILE *fpOutput = fopen(OUTPUT_FILE, "wb");
+ ASSERT_NE(fpOutput, nullptr) << "Error opening output file " << OUTPUT_FILE;
+
+ // Write file header.
+ fwrite("#!AMR\n", 1, 6, fpOutput);
+
+ int32_t mode = GetParam().second;
+ // Encode kNumFrameReset first
+ int32_t encodeErr = EncodeFrames(mode, fpInput, fpOutput, kNumFrameReset);
+ ASSERT_EQ(encodeErr, 0) << "EncodeFrames returned error for Codec mode: " << mode;
+
+ status = AMREncodeReset(mAmrEncHandle->encCtx, mAmrEncHandle->pidSyncCtx);
+ ASSERT_EQ(status, 0) << "Error resting AMR-NB encoder";
+
+ // Start encoding again
+ encodeErr = EncodeFrames(mode, fpInput, fpOutput);
+ ASSERT_EQ(encodeErr, 0) << "EncodeFrames returned error for Codec mode: " << mode;
+
+ fclose(fpOutput);
+ fclose(fpInput);
+
+ AMREncodeExit(&mAmrEncHandle->encCtx, &mAmrEncHandle->pidSyncCtx);
+ ASSERT_EQ(mAmrEncHandle->encCtx, nullptr) << "Error deleting AMR-NB encoder";
+ ASSERT_EQ(mAmrEncHandle->pidSyncCtx, nullptr) << "Error deleting AMR-NB encoder";
+ free(mAmrEncHandle);
+ mAmrEncHandle = nullptr;
+ ALOGV("Successfully deleted encoder");
+}
+
+// TODO: Add more test vectors
+INSTANTIATE_TEST_SUITE_P(AmrnbEncoderTestAll, AmrnbEncoderTest,
+ ::testing::Values(make_pair("bbb_raw_1ch_8khz_s16le.raw", MR475),
+ make_pair("bbb_raw_1ch_8khz_s16le.raw", MR515),
+ make_pair("bbb_raw_1ch_8khz_s16le.raw", MR59),
+ make_pair("bbb_raw_1ch_8khz_s16le.raw", MR67),
+ make_pair("bbb_raw_1ch_8khz_s16le.raw", MR74),
+ make_pair("bbb_raw_1ch_8khz_s16le.raw", MR795),
+ make_pair("bbb_raw_1ch_8khz_s16le.raw", MR102),
+ make_pair("bbb_raw_1ch_8khz_s16le.raw", MR122),
+ make_pair("sinesweepraw.raw", MR475),
+ make_pair("sinesweepraw.raw", MR515),
+ make_pair("sinesweepraw.raw", MR59),
+ make_pair("sinesweepraw.raw", MR67),
+ make_pair("sinesweepraw.raw", MR74),
+ make_pair("sinesweepraw.raw", MR795),
+ make_pair("sinesweepraw.raw", MR102),
+ make_pair("sinesweepraw.raw", MR122)));
+
+int main(int argc, char **argv) {
+ gEnv = new AmrnbEncTestEnvironment();
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = gEnv->initFromOptions(argc, argv);
+ if (status == 0) {
+ status = RUN_ALL_TESTS();
+ ALOGV("Test result = %d\n", status);
+ }
+ return status;
+}
diff --git a/media/libstagefright/codecs/amrnb/enc/test/Android.bp b/media/libstagefright/codecs/amrnb/enc/test/Android.bp
new file mode 100644
index 0000000..e8982fe
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/enc/test/Android.bp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+cc_test {
+ name: "AmrnbEncoderTest",
+ gtest: true,
+
+ srcs: [
+ "AmrnbEncoderTest.cpp",
+ ],
+
+ static_libs: [
+ "libstagefright_amrnb_common",
+ "libstagefright_amrnbenc",
+ "libaudioutils",
+ "libsndfile",
+ ],
+
+ shared_libs: [
+ "liblog",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ sanitize: {
+ cfi: true,
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ },
+}
diff --git a/media/libstagefright/codecs/amrnb/enc/test/README.md b/media/libstagefright/codecs/amrnb/enc/test/README.md
new file mode 100644
index 0000000..4ddaa57
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/enc/test/README.md
@@ -0,0 +1,34 @@
+## Media Testing ##
+---
+#### AMR-NB Encoder :
+The Amr-Nb Encoder Test Suite validates the amrnb encoder available in libstagefright.
+
+Run the following steps to build the test suite:
+```
+m AmrnbEncoderTest
+```
+
+The 32-bit binaries will be created in the following path : ${OUT}/data/nativetest/
+
+The 64-bit binaries will be created in the following path : ${OUT}/data/nativetest64/
+
+To test 64-bit binary push binaries from nativetest64.
+```
+adb push ${OUT}/data/nativetest64/AmrnbEncoderTest/AmrnbEncoderTest /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/AmrnbEncoderTest/AmrnbEncoderTest /data/local/tmp/
+```
+
+The resource file for the tests is taken from [here](https://drive.google.com/drive/folders/13cM4tAaVFrmr-zGFqaAzFBbKs75pnm9b). Push these files into device for testing.
+Download amr-nb_encoder folder and push all the files in this folder to /data/local/tmp/ on the device.
+```
+adb push amr-nb_encoder/. /data/local/tmp/
+```
+
+usage: AmrnbEncoderTest -P \<path_to_folder\>
+```
+adb shell /data/local/tmp/AmrnbEncoderTest -P /data/local/tmp/
+```
diff --git a/media/libstagefright/codecs/amrwb/test/AmrwbDecTestEnvironment.h b/media/libstagefright/codecs/amrwb/test/AmrwbDecTestEnvironment.h
new file mode 100644
index 0000000..84d337d
--- /dev/null
+++ b/media/libstagefright/codecs/amrwb/test/AmrwbDecTestEnvironment.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __AMRWBDEC_TEST_ENVIRONMENT_H__
+#define __AMRWBDEC_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class AmrwbDecTestEnvironment : public ::testing::Environment {
+ public:
+ AmrwbDecTestEnvironment() : res("/data/local/tmp/") {}
+
+ // Parses the command line arguments
+ int initFromOptions(int argc, char **argv);
+
+ void setRes(const char *_res) { res = _res; }
+
+ const string getRes() const { return res; }
+
+ private:
+ string res;
+};
+
+int AmrwbDecTestEnvironment::initFromOptions(int argc, char **argv) {
+ static struct option options[] = {{"res", required_argument, 0, 'P'}, {0, 0, 0, 0}};
+
+ while (true) {
+ int index = 0;
+ int c = getopt_long(argc, argv, "P:", options, &index);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'P':
+ setRes(optarg);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr,
+ "unrecognized option: %s\n\n"
+ "usage: %s <gtest options> <test options>\n\n"
+ "test options are:\n\n"
+ "-P, --path: Resource files directory location\n",
+ argv[optind ?: 1], argv[0]);
+ return 2;
+ }
+ return 0;
+}
+
+#endif // __AMRWBDEC_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/codecs/amrwb/test/AmrwbDecoderTest.cpp b/media/libstagefright/codecs/amrwb/test/AmrwbDecoderTest.cpp
new file mode 100644
index 0000000..2aad81b
--- /dev/null
+++ b/media/libstagefright/codecs/amrwb/test/AmrwbDecoderTest.cpp
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2019 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 "AmrwbDecoderTest"
+#define OUTPUT_FILE "/data/local/tmp/amrwbDecode.out"
+
+#include <utils/Log.h>
+
+#include <audio_utils/sndfile.h>
+#include <stdio.h>
+
+#include "pvamrwbdecoder.h"
+#include "pvamrwbdecoder_api.h"
+
+#include "AmrwbDecTestEnvironment.h"
+
+// Constants for AMR-WB.
+constexpr int32_t kInputBufferSize = 64;
+constexpr int32_t kSamplesPerFrame = 320;
+constexpr int32_t kBitsPerSample = 16;
+constexpr int32_t kSampleRate = 16000;
+constexpr int32_t kChannels = 1;
+constexpr int32_t kMaxSourceDataUnitSize = KAMRWB_NB_BITS_MAX * sizeof(int16_t);
+constexpr int32_t kOutputBufferSize = kSamplesPerFrame * kBitsPerSample / 8;
+const int32_t kFrameSizes[16] = {17, 23, 32, 36, 40, 46, 50, 58, 60, -1, -1, -1, -1, -1, -1, -1};
+constexpr int32_t kNumFrameReset = 150;
+
+constexpr int32_t kMaxCount = 10;
+
+static AmrwbDecTestEnvironment *gEnv = nullptr;
+
+class AmrwbDecoderTest : public ::testing::TestWithParam<string> {
+ public:
+ AmrwbDecoderTest() : mFpInput(nullptr) {}
+
+ ~AmrwbDecoderTest() {
+ if (mFpInput) {
+ fclose(mFpInput);
+ mFpInput = nullptr;
+ }
+ }
+
+ FILE *mFpInput;
+ int32_t DecodeFrames(int16_t *decoderCookie, void *decoderBuf, SNDFILE *outFileHandle,
+ int32_t frameCount = INT32_MAX);
+ SNDFILE *openOutputFile(SF_INFO *sfInfo);
+};
+
+SNDFILE *AmrwbDecoderTest::openOutputFile(SF_INFO *sfInfo) {
+ memset(sfInfo, 0, sizeof(SF_INFO));
+ sfInfo->channels = kChannels;
+ sfInfo->format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
+ sfInfo->samplerate = kSampleRate;
+ SNDFILE *outFileHandle = sf_open(OUTPUT_FILE, SFM_WRITE, sfInfo);
+ return outFileHandle;
+}
+
+int32_t AmrwbDecoderTest::DecodeFrames(int16_t *decoderCookie, void *decoderBuf,
+ SNDFILE *outFileHandle, int32_t frameCount) {
+ uint8_t inputBuf[kInputBufferSize];
+ int16_t inputSampleBuf[kMaxSourceDataUnitSize];
+ int16_t outputBuf[kOutputBufferSize];
+
+ while (frameCount > 0) {
+ uint8_t modeByte;
+ int32_t bytesRead = fread(&modeByte, 1, 1, mFpInput);
+ if (bytesRead != 1) break;
+
+ int16 mode = ((modeByte >> 3) & 0x0f);
+ if (mode >= 9) {
+ // Produce silence for comfort noise, speech lost and no data.
+ int32_t outputBufferSize = kSamplesPerFrame * kBitsPerSample / 8;
+ memset(outputBuf, 0, outputBufferSize);
+ } else {
+ // Read rest of the frame.
+ int32_t frameSize = kFrameSizes[mode];
+ // AMR-WB file format cannot have mode 10, 11, 12 and 13.
+ if (frameSize < 0) {
+ ALOGE("Illegal frame mode");
+ return -1;
+ }
+ bytesRead = fread(inputBuf, 1, frameSize, mFpInput);
+ if (bytesRead != frameSize) break;
+
+ int16 frameMode = mode;
+ int16 frameType;
+ RX_State_wb rx_state;
+ mime_unsorting(inputBuf, inputSampleBuf, &frameType, &frameMode, 1, &rx_state);
+
+ int16_t numSamplesOutput;
+ pvDecoder_AmrWb(frameMode, inputSampleBuf, outputBuf, &numSamplesOutput, decoderBuf,
+ frameType, decoderCookie);
+ if (numSamplesOutput != kSamplesPerFrame) {
+ ALOGE("Failed to decode the input file");
+ return -1;
+ }
+ for (int count = 0; count < kSamplesPerFrame; ++count) {
+ /* Delete the 2 LSBs (14-bit output) */
+ outputBuf[count] &= 0xfffc;
+ }
+ }
+ sf_writef_short(outFileHandle, outputBuf, kSamplesPerFrame / kChannels);
+ frameCount--;
+ }
+ return 0;
+}
+
+TEST_F(AmrwbDecoderTest, MultiCreateAmrwbDecoderTest) {
+ uint32_t memRequirements = pvDecoder_AmrWbMemRequirements();
+ void *decoderBuf = malloc(memRequirements);
+ ASSERT_NE(decoderBuf, nullptr)
+ << "Failed to allocate decoder memory of size " << memRequirements;
+
+ // Create AMR-WB decoder instance.
+ void *amrHandle = nullptr;
+ int16_t *decoderCookie;
+ for (int count = 0; count < kMaxCount; count++) {
+ pvDecoder_AmrWb_Init(&amrHandle, decoderBuf, &decoderCookie);
+ ASSERT_NE(amrHandle, nullptr) << "Failed to initialize decoder";
+ ALOGV("Decoder created successfully");
+ }
+ if (decoderBuf) {
+ free(decoderBuf);
+ decoderBuf = nullptr;
+ }
+}
+
+TEST_P(AmrwbDecoderTest, DecodeTest) {
+ uint32_t memRequirements = pvDecoder_AmrWbMemRequirements();
+ void *decoderBuf = malloc(memRequirements);
+ ASSERT_NE(decoderBuf, nullptr)
+ << "Failed to allocate decoder memory of size " << memRequirements;
+
+ void *amrHandle = nullptr;
+ int16_t *decoderCookie;
+ pvDecoder_AmrWb_Init(&amrHandle, decoderBuf, &decoderCookie);
+ ASSERT_NE(amrHandle, nullptr) << "Failed to initialize decoder";
+
+ string inputFile = gEnv->getRes() + GetParam();
+ mFpInput = fopen(inputFile.c_str(), "rb");
+ ASSERT_NE(mFpInput, nullptr) << "Error opening input file " << inputFile;
+
+ // Open the output file.
+ SF_INFO sfInfo;
+ SNDFILE *outFileHandle = openOutputFile(&sfInfo);
+ ASSERT_NE(outFileHandle, nullptr) << "Error opening output file for writing decoded output";
+
+ int32_t decoderErr = DecodeFrames(decoderCookie, decoderBuf, outFileHandle);
+ ASSERT_EQ(decoderErr, 0) << "DecodeFrames returned error";
+
+ sf_close(outFileHandle);
+ if (decoderBuf) {
+ free(decoderBuf);
+ decoderBuf = nullptr;
+ }
+}
+
+TEST_P(AmrwbDecoderTest, ResetDecoderTest) {
+ uint32_t memRequirements = pvDecoder_AmrWbMemRequirements();
+ void *decoderBuf = malloc(memRequirements);
+ ASSERT_NE(decoderBuf, nullptr)
+ << "Failed to allocate decoder memory of size " << memRequirements;
+
+ void *amrHandle = nullptr;
+ int16_t *decoderCookie;
+ pvDecoder_AmrWb_Init(&amrHandle, decoderBuf, &decoderCookie);
+ ASSERT_NE(amrHandle, nullptr) << "Failed to initialize decoder";
+
+ string inputFile = gEnv->getRes() + GetParam();
+ mFpInput = fopen(inputFile.c_str(), "rb");
+ ASSERT_NE(mFpInput, nullptr) << "Error opening input file " << inputFile;
+
+ // Open the output file.
+ SF_INFO sfInfo;
+ SNDFILE *outFileHandle = openOutputFile(&sfInfo);
+ ASSERT_NE(outFileHandle, nullptr) << "Error opening output file for writing decoded output";
+
+ // Decode 150 frames first
+ int32_t decoderErr = DecodeFrames(decoderCookie, decoderBuf, outFileHandle, kNumFrameReset);
+ ASSERT_EQ(decoderErr, 0) << "DecodeFrames returned error";
+
+ // Reset Decoder
+ pvDecoder_AmrWb_Reset(decoderBuf, 1);
+
+ // Start decoding again
+ decoderErr = DecodeFrames(decoderCookie, decoderBuf, outFileHandle);
+ ASSERT_EQ(decoderErr, 0) << "DecodeFrames returned error";
+
+ sf_close(outFileHandle);
+ if (decoderBuf) {
+ free(decoderBuf);
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(AmrwbDecoderTestAll, AmrwbDecoderTest,
+ ::testing::Values(("bbb_amrwb_1ch_14kbps_16000hz.amrwb"),
+ ("bbb_16000hz_1ch_9kbps_amrwb_30sec.amrwb")));
+
+int main(int argc, char **argv) {
+ gEnv = new AmrwbDecTestEnvironment();
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = gEnv->initFromOptions(argc, argv);
+ if (status == 0) {
+ status = RUN_ALL_TESTS();
+ ALOGV("Test result = %d\n", status);
+ }
+ return status;
+}
diff --git a/media/libstagefright/codecs/amrwb/test/Android.bp b/media/libstagefright/codecs/amrwb/test/Android.bp
new file mode 100644
index 0000000..968215a
--- /dev/null
+++ b/media/libstagefright/codecs/amrwb/test/Android.bp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+cc_test {
+ name: "AmrwbDecoderTest",
+ gtest: true,
+
+ srcs: [
+ "AmrwbDecoderTest.cpp",
+ ],
+
+ static_libs: [
+ "libstagefright_amrwbdec",
+ "libsndfile",
+ "libaudioutils",
+ ],
+
+ shared_libs: [
+ "liblog",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ sanitize: {
+ cfi: true,
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ },
+}
diff --git a/media/libstagefright/codecs/amrwb/test/README.md b/media/libstagefright/codecs/amrwb/test/README.md
new file mode 100644
index 0000000..502a20d
--- /dev/null
+++ b/media/libstagefright/codecs/amrwb/test/README.md
@@ -0,0 +1,34 @@
+## Media Testing ##
+---
+#### AMR-WB Decoder :
+The Amr-Wb Decoder Test Suite validates the amrwb decoder available in libstagefright.
+
+Run the following steps to build the test suite:
+```
+m AmrwbDecoderTest
+```
+
+The 32-bit binaries will be created in the following path : ${OUT}/data/nativetest/
+
+The 64-bit binaries will be created in the following path : ${OUT}/data/nativetest64/
+
+To test 64-bit binary push binaries from nativetest64.
+```
+adb push ${OUT}/data/nativetest64/AmrwbDecoderTest/AmrwbDecoderTest /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/AmrwbDecoderTest/AmrwbDecoderTest /data/local/tmp/
+```
+
+The resource file for the tests is taken from [here](https://drive.google.com/drive/folders/13cM4tAaVFrmr-zGFqaAzFBbKs75pnm9b). Push these files into device for testing.
+Download amr-wb folder and push all the files in this folder to /data/local/tmp/ on the device.
+```
+adb push amr-wb/. /data/local/tmp/
+```
+
+usage: AmrwbDecoderTest -P \<path_to_folder\>
+```
+adb shell /data/local/tmp/AmrwbDecoderTest -P /data/local/tmp/
+```
diff --git a/media/libstagefright/codecs/amrwbenc/test/AmrwbEncTestEnvironment.h b/media/libstagefright/codecs/amrwbenc/test/AmrwbEncTestEnvironment.h
new file mode 100644
index 0000000..08ada66
--- /dev/null
+++ b/media/libstagefright/codecs/amrwbenc/test/AmrwbEncTestEnvironment.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __AMRWBENC_TEST_ENVIRONMENT_H__
+#define __AMRWBENC_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class AmrwbEncTestEnvironment : public ::testing::Environment {
+ public:
+ AmrwbEncTestEnvironment() : res("/data/local/tmp/") {}
+
+ // Parses the command line arguments
+ int initFromOptions(int argc, char **argv);
+
+ void setRes(const char *_res) { res = _res; }
+
+ const string getRes() const { return res; }
+
+ private:
+ string res;
+};
+
+int AmrwbEncTestEnvironment::initFromOptions(int argc, char **argv) {
+ static struct option options[] = {{"res", required_argument, 0, 'P'}, {0, 0, 0, 0}};
+
+ while (true) {
+ int index = 0;
+ int c = getopt_long(argc, argv, "P:", options, &index);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'P':
+ setRes(optarg);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr,
+ "unrecognized option: %s\n\n"
+ "usage: %s <gtest options> <test options>\n\n"
+ "test options are:\n\n"
+ "-P, --path: Resource files directory location\n",
+ argv[optind ?: 1], argv[0]);
+ return 2;
+ }
+ return 0;
+}
+
+#endif // __AMRWBENC_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/codecs/amrwbenc/test/AmrwbEncoderTest.cpp b/media/libstagefright/codecs/amrwbenc/test/AmrwbEncoderTest.cpp
new file mode 100644
index 0000000..1a6ee27
--- /dev/null
+++ b/media/libstagefright/codecs/amrwbenc/test/AmrwbEncoderTest.cpp
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2019 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 "AmrwbEncoderTest"
+
+#include <utils/Log.h>
+
+#include <stdio.h>
+
+#include "cmnMemory.h"
+#include "voAMRWB.h"
+
+#include "AmrwbEncTestEnvironment.h"
+
+#define OUTPUT_FILE "/data/local/tmp/amrwbEncode.out"
+#define VOAMRWB_RFC3267_HEADER_INFO "#!AMR-WB\n"
+
+constexpr int32_t kInputBufferSize = 640;
+constexpr int32_t kOutputBufferSize = 1024;
+
+static AmrwbEncTestEnvironment *gEnv = nullptr;
+
+class AmrwbEncoderTest : public ::testing::TestWithParam<tuple<string, int32_t, VOAMRWBFRAMETYPE>> {
+ public:
+ AmrwbEncoderTest() : mEncoderHandle(nullptr) {
+ tuple<string, int32_t, VOAMRWBFRAMETYPE> params = GetParam();
+ mInputFile = gEnv->getRes() + get<0>(params);
+ mMode = get<1>(params);
+ mFrameType = get<2>(params);
+ mMemOperator.Alloc = cmnMemAlloc;
+ mMemOperator.Copy = cmnMemCopy;
+ mMemOperator.Free = cmnMemFree;
+ mMemOperator.Set = cmnMemSet;
+ mMemOperator.Check = cmnMemCheck;
+
+ mUserData.memflag = VO_IMF_USERMEMOPERATOR;
+ mUserData.memData = (VO_PTR)(&mMemOperator);
+ }
+
+ ~AmrwbEncoderTest() {
+ if (mEncoderHandle) {
+ mEncoderHandle = nullptr;
+ }
+ }
+
+ string mInputFile;
+ unsigned char mOutputBuf[kOutputBufferSize];
+ unsigned char mInputBuf[kInputBufferSize];
+ VOAMRWBFRAMETYPE mFrameType;
+ VO_AUDIO_CODECAPI mApiHandle;
+ VO_MEM_OPERATOR mMemOperator;
+ VO_CODEC_INIT_USERDATA mUserData;
+ VO_HANDLE mEncoderHandle;
+ int32_t mMode;
+};
+
+TEST_P(AmrwbEncoderTest, CreateAmrwbEncoderTest) {
+ int32_t status = voGetAMRWBEncAPI(&mApiHandle);
+ ASSERT_EQ(status, VO_ERR_NONE) << "Failed to get api handle";
+
+ status = mApiHandle.Init(&mEncoderHandle, VO_AUDIO_CodingAMRWB, &mUserData);
+ ASSERT_EQ(status, VO_ERR_NONE) << "Failed to init AMRWB encoder";
+
+ status = mApiHandle.SetParam(mEncoderHandle, VO_PID_AMRWB_FRAMETYPE, &mFrameType);
+ ASSERT_EQ(status, VO_ERR_NONE) << "Failed to set AMRWB encoder frame type to " << mFrameType;
+
+ status = mApiHandle.SetParam(mEncoderHandle, VO_PID_AMRWB_MODE, &mMode);
+ ASSERT_EQ(status, VO_ERR_NONE) << "Failed to set AMRWB encoder mode to %d" << mMode;
+ ALOGV("AMR-WB encoder created successfully");
+
+ status = mApiHandle.Uninit(mEncoderHandle);
+ ASSERT_EQ(status, VO_ERR_NONE) << "Failed to delete AMRWB encoder";
+ ALOGV("AMR-WB encoder deleted successfully");
+}
+
+TEST_P(AmrwbEncoderTest, AmrwbEncodeTest) {
+ VO_CODECBUFFER inData;
+ VO_CODECBUFFER outData;
+ VO_AUDIO_OUTPUTINFO outFormat;
+
+ FILE *fpInput = fopen(mInputFile.c_str(), "rb");
+ ASSERT_NE(fpInput, nullptr) << "Error opening input file " << mInputFile;
+
+ FILE *fpOutput = fopen(OUTPUT_FILE, "wb");
+ ASSERT_NE(fpOutput, nullptr) << "Error opening output file " << OUTPUT_FILE;
+
+ uint32_t status = voGetAMRWBEncAPI(&mApiHandle);
+ ASSERT_EQ(status, VO_ERR_NONE) << "Failed to get api handle";
+
+ status = mApiHandle.Init(&mEncoderHandle, VO_AUDIO_CodingAMRWB, &mUserData);
+ ASSERT_EQ(status, VO_ERR_NONE) << "Failed to init AMRWB encoder";
+
+ status = mApiHandle.SetParam(mEncoderHandle, VO_PID_AMRWB_FRAMETYPE, &mFrameType);
+ ASSERT_EQ(status, VO_ERR_NONE) << "Failed to set AMRWB encoder frame type to " << mFrameType;
+
+ status = mApiHandle.SetParam(mEncoderHandle, VO_PID_AMRWB_MODE, &mMode);
+ ASSERT_EQ(status, VO_ERR_NONE) << "Failed to set AMRWB encoder mode to " << mMode;
+
+ if (mFrameType == VOAMRWB_RFC3267) {
+ /* write RFC3267 Header info to indicate single channel AMR file storage format */
+ int32_t size = strlen(VOAMRWB_RFC3267_HEADER_INFO);
+ memcpy(mOutputBuf, VOAMRWB_RFC3267_HEADER_INFO, size);
+ fwrite(mOutputBuf, 1, size, fpOutput);
+ }
+
+ int32_t frameNum = 0;
+ while (1) {
+ int32_t buffLength =
+ (int32_t)fread(mInputBuf, sizeof(signed char), kInputBufferSize, fpInput);
+
+ if (buffLength == 0 || feof(fpInput)) break;
+ ASSERT_EQ(buffLength, kInputBufferSize) << "Error in reading input file";
+
+ inData.Buffer = (unsigned char *)mInputBuf;
+ inData.Length = buffLength;
+ outData.Buffer = mOutputBuf;
+ status = mApiHandle.SetInputData(mEncoderHandle, &inData);
+ ASSERT_EQ(status, VO_ERR_NONE) << "Failed to setup Input data";
+
+ do {
+ status = mApiHandle.GetOutputData(mEncoderHandle, &outData, &outFormat);
+ ASSERT_NE(status, VO_ERR_LICENSE_ERROR) << "Failed to encode the file";
+ if (status == 0) {
+ frameNum++;
+ fwrite(outData.Buffer, 1, outData.Length, fpOutput);
+ fflush(fpOutput);
+ }
+ } while (status != VO_ERR_INPUT_BUFFER_SMALL);
+ }
+
+ ALOGV("Number of frames processed: %d", frameNum);
+ status = mApiHandle.Uninit(mEncoderHandle);
+ ASSERT_EQ(status, VO_ERR_NONE) << "Failed to delete AMRWB encoder";
+
+ if (fpInput) {
+ fclose(fpInput);
+ }
+ if (fpOutput) {
+ fclose(fpOutput);
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AmrwbEncoderTestAll, AmrwbEncoderTest,
+ ::testing::Values(
+ make_tuple("bbb_raw_1ch_16khz_s16le.raw", VOAMRWB_MD66, VOAMRWB_DEFAULT),
+ make_tuple("bbb_raw_1ch_16khz_s16le.raw", VOAMRWB_MD885, VOAMRWB_DEFAULT),
+ make_tuple("bbb_raw_1ch_16khz_s16le.raw", VOAMRWB_MD1265, VOAMRWB_DEFAULT),
+ make_tuple("bbb_raw_1ch_16khz_s16le.raw", VOAMRWB_MD1425, VOAMRWB_DEFAULT),
+ make_tuple("bbb_raw_1ch_16khz_s16le.raw", VOAMRWB_MD1585, VOAMRWB_DEFAULT),
+ make_tuple("bbb_raw_1ch_16khz_s16le.raw", VOAMRWB_MD1825, VOAMRWB_DEFAULT),
+ make_tuple("bbb_raw_1ch_16khz_s16le.raw", VOAMRWB_MD1985, VOAMRWB_DEFAULT),
+ make_tuple("bbb_raw_1ch_16khz_s16le.raw", VOAMRWB_MD2305, VOAMRWB_DEFAULT),
+ make_tuple("bbb_raw_1ch_16khz_s16le.raw", VOAMRWB_MD2385, VOAMRWB_DEFAULT),
+ make_tuple("bbb_raw_1ch_16khz_s16le.raw", VOAMRWB_MD66, VOAMRWB_ITU),
+ make_tuple("bbb_raw_1ch_16khz_s16le.raw", VOAMRWB_MD885, VOAMRWB_ITU),
+ make_tuple("bbb_raw_1ch_16khz_s16le.raw", VOAMRWB_MD1265, VOAMRWB_ITU),
+ make_tuple("bbb_raw_1ch_16khz_s16le.raw", VOAMRWB_MD1425, VOAMRWB_ITU),
+ make_tuple("bbb_raw_1ch_16khz_s16le.raw", VOAMRWB_MD1585, VOAMRWB_ITU),
+ make_tuple("bbb_raw_1ch_16khz_s16le.raw", VOAMRWB_MD1825, VOAMRWB_ITU),
+ make_tuple("bbb_raw_1ch_16khz_s16le.raw", VOAMRWB_MD1985, VOAMRWB_ITU),
+ make_tuple("bbb_raw_1ch_16khz_s16le.raw", VOAMRWB_MD2305, VOAMRWB_ITU),
+ make_tuple("bbb_raw_1ch_16khz_s16le.raw", VOAMRWB_MD2385, VOAMRWB_ITU),
+ make_tuple("bbb_raw_1ch_16khz_s16le.raw", VOAMRWB_MD66, VOAMRWB_RFC3267),
+ make_tuple("bbb_raw_1ch_16khz_s16le.raw", VOAMRWB_MD885, VOAMRWB_RFC3267),
+ make_tuple("bbb_raw_1ch_16khz_s16le.raw", VOAMRWB_MD1265, VOAMRWB_RFC3267),
+ make_tuple("bbb_raw_1ch_16khz_s16le.raw", VOAMRWB_MD1425, VOAMRWB_RFC3267),
+ make_tuple("bbb_raw_1ch_16khz_s16le.raw", VOAMRWB_MD1585, VOAMRWB_RFC3267),
+ make_tuple("bbb_raw_1ch_16khz_s16le.raw", VOAMRWB_MD1825, VOAMRWB_RFC3267),
+ make_tuple("bbb_raw_1ch_16khz_s16le.raw", VOAMRWB_MD1985, VOAMRWB_RFC3267),
+ make_tuple("bbb_raw_1ch_16khz_s16le.raw", VOAMRWB_MD2305, VOAMRWB_RFC3267),
+ make_tuple("bbb_raw_1ch_16khz_s16le.raw", VOAMRWB_MD2385, VOAMRWB_RFC3267)));
+
+int main(int argc, char **argv) {
+ gEnv = new AmrwbEncTestEnvironment();
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = gEnv->initFromOptions(argc, argv);
+ if (status == 0) {
+ status = RUN_ALL_TESTS();
+ ALOGV("Test result = %d\n", status);
+ }
+ return status;
+}
diff --git a/media/libstagefright/codecs/amrwbenc/test/Android.bp b/media/libstagefright/codecs/amrwbenc/test/Android.bp
new file mode 100644
index 0000000..7042bc5
--- /dev/null
+++ b/media/libstagefright/codecs/amrwbenc/test/Android.bp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+cc_test {
+ name: "AmrwbEncoderTest",
+ gtest: true,
+
+ srcs: [
+ "AmrwbEncoderTest.cpp",
+ ],
+
+ static_libs: [
+ "libstagefright_enc_common",
+ "libstagefright_amrwbenc",
+ "libaudioutils",
+ "libsndfile",
+ ],
+
+ shared_libs: [
+ "liblog",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ sanitize: {
+ cfi: true,
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ },
+}
diff --git a/media/libstagefright/codecs/amrwbenc/test/README.md b/media/libstagefright/codecs/amrwbenc/test/README.md
new file mode 100644
index 0000000..ea2135f
--- /dev/null
+++ b/media/libstagefright/codecs/amrwbenc/test/README.md
@@ -0,0 +1,34 @@
+## Media Testing ##
+---
+#### AMR-WB Encoder :
+The Amr-Wb Encoder Test Suite validates the amrwb encoder available in libstagefright.
+
+Run the following steps to build the test suite:
+```
+m AmrwbEncoderTest
+```
+
+The 32-bit binaries will be created in the following path : ${OUT}/data/nativetest/
+
+The 64-bit binaries will be created in the following path : ${OUT}/data/nativetest64/
+
+To test 64-bit binary push binaries from nativetest64.
+```
+adb push ${OUT}/data/nativetest64/AmrwbEncoderTest/AmrwbEncoderTest /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/AmrwbEncoderTest/AmrwbEncoderTest /data/local/tmp/
+```
+
+The resource file for the tests is taken from [here](https://drive.google.com/drive/folders/13cM4tAaVFrmr-zGFqaAzFBbKs75pnm9b). Push these files into device for testing.
+Download amr-wb_encoder folder and push all the files in this folder to /data/local/tmp/ on the device.
+```
+adb push amr-wb_encoder/. /data/local/tmp/
+```
+
+usage: AmrwbEncoderTest -P \<path_to_folder\>
+```
+adb shell /data/local/tmp/AmrwbEncoderTest -P /data/local/tmp/
+```
diff --git a/media/libstagefright/codecs/m4v_h263/dec/test/Android.bp b/media/libstagefright/codecs/m4v_h263/dec/test/Android.bp
new file mode 100644
index 0000000..e335c9b
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/dec/test/Android.bp
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+cc_test {
+ name: "Mpeg4H263DecoderTest",
+ gtest: true,
+
+ srcs: [
+ "Mpeg4H263DecoderTest.cpp",
+ ],
+
+ shared_libs: [
+ "liblog",
+ ],
+
+ static_libs: [
+ "libstagefright_m4vh263dec",
+ "libstagefright_foundation",
+ ],
+
+ cflags: [
+ "-DOSCL_IMPORT_REF=",
+ "-Werror",
+ "-Wall",
+ ],
+
+ sanitize: {
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ cfi: true,
+ },
+}
diff --git a/media/libstagefright/codecs/m4v_h263/dec/test/Mpeg4H263DecoderTest.cpp b/media/libstagefright/codecs/m4v_h263/dec/test/Mpeg4H263DecoderTest.cpp
new file mode 100644
index 0000000..967c1ea
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/dec/test/Mpeg4H263DecoderTest.cpp
@@ -0,0 +1,423 @@
+/*
+ * 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 "Mpeg4H263DecoderTest"
+#include <utils/Log.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <utils/String8.h>
+#include <fstream>
+
+#include <media/stagefright/foundation/AUtils.h>
+#include "mp4dec_api.h"
+
+#include "Mpeg4H263DecoderTestEnvironment.h"
+
+using namespace android;
+
+#define OUTPUT_FILE_NAME "/data/local/tmp/Output.yuv"
+#define CODEC_CONFIG_FLAG 32
+#define SYNC_FRAME 1
+#define MPEG4_MAX_WIDTH 1920
+#define MPEG4_MAX_HEIGHT 1080
+#define H263_MAX_WIDTH 352
+#define H263_MAX_HEIGHT 288
+
+constexpr uint32_t kNumOutputBuffers = 2;
+
+struct FrameInfo {
+ int32_t bytesCount;
+ uint32_t flags;
+ int64_t timestamp;
+};
+
+struct tagvideoDecControls;
+
+static Mpeg4H263DecoderTestEnvironment *gEnv = nullptr;
+
+class Mpeg4H263DecoderTest : public ::testing::TestWithParam<tuple<string, string, bool>> {
+ public:
+ Mpeg4H263DecoderTest()
+ : mDecHandle(nullptr),
+ mInputBuffer(nullptr),
+ mInitialized(false),
+ mFramesConfigured(false),
+ mNumSamplesOutput(0),
+ mWidth(352),
+ mHeight(288) {
+ memset(mOutputBuffer, 0x0, sizeof(mOutputBuffer));
+ }
+
+ ~Mpeg4H263DecoderTest() {
+ if (mEleStream.is_open()) mEleStream.close();
+ if (mDecHandle) {
+ delete mDecHandle;
+ mDecHandle = nullptr;
+ }
+ if (mInputBuffer) {
+ free(mInputBuffer);
+ mInputBuffer = nullptr;
+ }
+ freeOutputBuffer();
+ }
+
+ status_t initDecoder();
+ void allocOutputBuffer(size_t outputBufferSize);
+ void dumpOutput(ofstream &ostrm);
+ void freeOutputBuffer();
+ void processMpeg4H263Decoder(vector<FrameInfo> Info, int32_t offset, int32_t range,
+ ifstream &mEleStream, ofstream &ostrm, MP4DecodingMode inputMode);
+ void deInitDecoder();
+
+ ifstream mEleStream;
+ tagvideoDecControls *mDecHandle;
+ char *mInputBuffer;
+ uint8_t *mOutputBuffer[kNumOutputBuffers];
+ bool mInitialized;
+ bool mFramesConfigured;
+ uint32_t mNumSamplesOutput;
+ uint32_t mWidth;
+ uint32_t mHeight;
+};
+
+status_t Mpeg4H263DecoderTest::initDecoder() {
+ if (!mDecHandle) {
+ mDecHandle = new tagvideoDecControls;
+ }
+ if (!mDecHandle) {
+ return NO_MEMORY;
+ }
+ memset(mDecHandle, 0, sizeof(tagvideoDecControls));
+
+ return OK;
+}
+
+void Mpeg4H263DecoderTest::allocOutputBuffer(size_t outputBufferSize) {
+ for (int32_t i = 0; i < kNumOutputBuffers; ++i) {
+ if (!mOutputBuffer[i]) {
+ mOutputBuffer[i] = (uint8_t *)malloc(outputBufferSize);
+ ASSERT_NE(mOutputBuffer[i], nullptr) << "Output buffer allocation failed";
+ }
+ }
+}
+
+void Mpeg4H263DecoderTest::dumpOutput(ofstream &ostrm) {
+ uint8_t *src = mOutputBuffer[mNumSamplesOutput & 1];
+ size_t vStride = align(mHeight, 16);
+ size_t srcYStride = align(mWidth, 16);
+ size_t srcUVStride = srcYStride / 2;
+ uint8_t *srcStart = src;
+
+ /* Y buffer */
+ for (size_t i = 0; i < mHeight; ++i) {
+ ostrm.write(reinterpret_cast<char *>(src), mWidth);
+ src += srcYStride;
+ }
+ /* U buffer */
+ src = srcStart + vStride * srcYStride;
+ for (size_t i = 0; i < mHeight / 2; ++i) {
+ ostrm.write(reinterpret_cast<char *>(src), mWidth / 2);
+ src += srcUVStride;
+ }
+ /* V buffer */
+ src = srcStart + vStride * srcYStride * 5 / 4;
+ for (size_t i = 0; i < mHeight / 2; ++i) {
+ ostrm.write(reinterpret_cast<char *>(src), mWidth / 2);
+ src += srcUVStride;
+ }
+}
+
+void Mpeg4H263DecoderTest::freeOutputBuffer() {
+ for (int32_t i = 0; i < kNumOutputBuffers; ++i) {
+ if (mOutputBuffer[i]) {
+ free(mOutputBuffer[i]);
+ mOutputBuffer[i] = nullptr;
+ }
+ }
+}
+
+void Mpeg4H263DecoderTest::processMpeg4H263Decoder(vector<FrameInfo> Info, int32_t offset,
+ int32_t range, ifstream &mEleStream,
+ ofstream &ostrm, MP4DecodingMode inputMode) {
+ size_t maxWidth = (inputMode == MPEG4_MODE) ? MPEG4_MAX_WIDTH : H263_MAX_WIDTH;
+ size_t maxHeight = (inputMode == MPEG4_MODE) ? MPEG4_MAX_HEIGHT : H263_MAX_HEIGHT;
+ size_t outputBufferSize = align(maxWidth, 16) * align(maxHeight, 16) * 3 / 2;
+ uint32_t frameIndex = offset;
+ bool status = true;
+ ASSERT_GE(range, 0) << "Invalid range";
+ ASSERT_TRUE(offset >= 0 && offset <= Info.size() - 1) << "Invalid offset";
+ ASSERT_LE(range + offset, Info.size()) << "range+offset can't be greater than the no of frames";
+
+ while (1) {
+ if (frameIndex == Info.size() || frameIndex == (offset + range)) break;
+
+ int32_t bytesCount = Info[frameIndex].bytesCount;
+ ASSERT_GT(bytesCount, 0) << "Size for the memory allocation is negative";
+ mInputBuffer = (char *)malloc(bytesCount);
+ ASSERT_NE(mInputBuffer, nullptr) << "Insufficient memory to read frame";
+ mEleStream.read(mInputBuffer, bytesCount);
+ ASSERT_EQ(mEleStream.gcount(), bytesCount) << "mEleStream.gcount() != bytesCount";
+ static const uint8_t volInfo[] = {0x00, 0x00, 0x01, 0xB0};
+ bool volHeader = memcmp(mInputBuffer, volInfo, 4) == 0;
+ if (volHeader) {
+ PVCleanUpVideoDecoder(mDecHandle);
+ mInitialized = false;
+ }
+
+ if (!mInitialized) {
+ uint8_t *volData[1]{};
+ int32_t volSize = 0;
+
+ uint32_t flags = Info[frameIndex].flags;
+ bool codecConfig = flags == CODEC_CONFIG_FLAG;
+ if (codecConfig || volHeader) {
+ volData[0] = reinterpret_cast<uint8_t *>(mInputBuffer);
+ volSize = bytesCount;
+ }
+
+ status = PVInitVideoDecoder(mDecHandle, volData, &volSize, 1, maxWidth, maxHeight,
+ inputMode);
+ ASSERT_TRUE(status) << "PVInitVideoDecoder failed. Unsupported content";
+
+ mInitialized = true;
+ MP4DecodingMode actualMode = PVGetDecBitstreamMode(mDecHandle);
+ ASSERT_EQ(inputMode, actualMode)
+ << "Decoded mode not same as actual mode of the decoder";
+
+ PVSetPostProcType(mDecHandle, 0);
+
+ int32_t dispWidth, dispHeight;
+ PVGetVideoDimensions(mDecHandle, &dispWidth, &dispHeight);
+
+ int32_t bufWidth, bufHeight;
+ PVGetBufferDimensions(mDecHandle, &bufWidth, &bufHeight);
+
+ ASSERT_LE(dispWidth, bufWidth) << "Display width is greater than buffer width";
+ ASSERT_LE(dispHeight, bufHeight) << "Display height is greater than buffer height";
+
+ if (dispWidth != mWidth || dispHeight != mHeight) {
+ mWidth = dispWidth;
+ mHeight = dispHeight;
+ freeOutputBuffer();
+ if (inputMode == H263_MODE) {
+ PVCleanUpVideoDecoder(mDecHandle);
+
+ uint8_t *volData[1]{};
+ int32_t volSize = 0;
+
+ status = PVInitVideoDecoder(mDecHandle, volData, &volSize, 1, maxWidth,
+ maxHeight, H263_MODE);
+ ASSERT_TRUE(status) << "PVInitVideoDecoder failed for H263";
+ }
+ mFramesConfigured = false;
+ }
+
+ if (codecConfig) {
+ frameIndex++;
+ continue;
+ }
+ }
+
+ uint32_t yFrameSize = sizeof(uint8) * mDecHandle->size;
+ ASSERT_GE(outputBufferSize, yFrameSize * 3 / 2)
+ << "Too small output buffer: " << outputBufferSize << " bytes";
+ ASSERT_NO_FATAL_FAILURE(allocOutputBuffer(outputBufferSize));
+
+ if (!mFramesConfigured) {
+ PVSetReferenceYUV(mDecHandle, mOutputBuffer[1]);
+ mFramesConfigured = true;
+ }
+
+ // Need to check if header contains new info, e.g., width/height, etc.
+ VopHeaderInfo headerInfo;
+ uint32_t useExtTimestamp = 1;
+ int32_t inputSize = (Info)[frameIndex].bytesCount;
+ uint32_t timestamp = frameIndex;
+
+ uint8_t *bitstreamTmp = reinterpret_cast<uint8_t *>(mInputBuffer);
+
+ status = PVDecodeVopHeader(mDecHandle, &bitstreamTmp, ×tamp, &inputSize, &headerInfo,
+ &useExtTimestamp, mOutputBuffer[mNumSamplesOutput & 1]);
+ ASSERT_EQ(status, PV_TRUE) << "failed to decode vop header";
+
+ // H263 doesn't have VOL header, the frame size information is in short header, i.e. the
+ // decoder may detect size change after PVDecodeVopHeader.
+ int32_t dispWidth, dispHeight;
+ PVGetVideoDimensions(mDecHandle, &dispWidth, &dispHeight);
+
+ int32_t bufWidth, bufHeight;
+ PVGetBufferDimensions(mDecHandle, &bufWidth, &bufHeight);
+
+ ASSERT_LE(dispWidth, bufWidth) << "Display width is greater than buffer width";
+ ASSERT_LE(dispHeight, bufHeight) << "Display height is greater than buffer height";
+ if (dispWidth != mWidth || dispHeight != mHeight) {
+ mWidth = dispWidth;
+ mHeight = dispHeight;
+ }
+
+ status = PVDecodeVopBody(mDecHandle, &inputSize);
+ ASSERT_EQ(status, PV_TRUE) << "failed to decode video frame No = %d" << frameIndex;
+
+ dumpOutput(ostrm);
+
+ ++mNumSamplesOutput;
+ ++frameIndex;
+ }
+ freeOutputBuffer();
+}
+
+void Mpeg4H263DecoderTest::deInitDecoder() {
+ if (mInitialized) {
+ if (mDecHandle) {
+ PVCleanUpVideoDecoder(mDecHandle);
+ delete mDecHandle;
+ mDecHandle = nullptr;
+ }
+ mInitialized = false;
+ }
+ freeOutputBuffer();
+}
+
+void getInfo(string infoFileName, vector<FrameInfo> &Info) {
+ ifstream eleInfo;
+ eleInfo.open(infoFileName);
+ ASSERT_EQ(eleInfo.is_open(), true) << "Failed to open " << infoFileName;
+ int32_t bytesCount = 0;
+ uint32_t flags = 0;
+ uint32_t timestamp = 0;
+ while (1) {
+ if (!(eleInfo >> bytesCount)) {
+ break;
+ }
+ eleInfo >> flags;
+ eleInfo >> timestamp;
+ Info.push_back({bytesCount, flags, timestamp});
+ }
+ if (eleInfo.is_open()) eleInfo.close();
+}
+
+TEST_P(Mpeg4H263DecoderTest, DecodeTest) {
+ tuple<string /* InputFileName */, string /* InfoFileName */, bool /* mode */> params =
+ GetParam();
+
+ string inputFileName = gEnv->getRes() + get<0>(params);
+ mEleStream.open(inputFileName, ifstream::binary);
+ ASSERT_EQ(mEleStream.is_open(), true) << "Failed to open " << get<0>(params);
+
+ string infoFileName = gEnv->getRes() + get<1>(params);
+ vector<FrameInfo> Info;
+ ASSERT_NO_FATAL_FAILURE(getInfo(infoFileName, Info));
+ ASSERT_NE(Info.empty(), true) << "Invalid Info file";
+
+ ofstream ostrm;
+ ostrm.open(OUTPUT_FILE_NAME, std::ofstream::binary);
+ ASSERT_EQ(ostrm.is_open(), true) << "Failed to open output stream for " << get<0>(params);
+
+ status_t err = initDecoder();
+ ASSERT_EQ(err, OK) << "initDecoder: failed to create decoder " << err;
+
+ bool isMpeg4 = get<2>(params);
+ MP4DecodingMode inputMode = isMpeg4 ? MPEG4_MODE : H263_MODE;
+ ASSERT_NO_FATAL_FAILURE(
+ processMpeg4H263Decoder(Info, 0, Info.size(), mEleStream, ostrm, inputMode));
+ deInitDecoder();
+ ostrm.close();
+ Info.clear();
+}
+
+TEST_P(Mpeg4H263DecoderTest, FlushTest) {
+ tuple<string /* InputFileName */, string /* InfoFileName */, bool /* mode */> params =
+ GetParam();
+
+ string inputFileName = gEnv->getRes() + get<0>(params);
+ mEleStream.open(inputFileName, ifstream::binary);
+ ASSERT_EQ(mEleStream.is_open(), true) << "Failed to open " << get<0>(params);
+
+ string infoFileName = gEnv->getRes() + get<1>(params);
+ vector<FrameInfo> Info;
+ ASSERT_NO_FATAL_FAILURE(getInfo(infoFileName, Info));
+ ASSERT_NE(Info.empty(), true) << "Invalid Info file";
+
+ ofstream ostrm;
+ ostrm.open(OUTPUT_FILE_NAME, std::ofstream::binary);
+ ASSERT_EQ(ostrm.is_open(), true) << "Failed to open output stream for " << get<0>(params);
+
+ status_t err = initDecoder();
+ ASSERT_EQ(err, OK) << "initDecoder: failed to create decoder " << err;
+
+ bool isMpeg4 = get<2>(params);
+ MP4DecodingMode inputMode = isMpeg4 ? MPEG4_MODE : H263_MODE;
+ // Number of frames to be decoded before flush
+ int32_t numFrames = Info.size() / 3;
+ ASSERT_NO_FATAL_FAILURE(
+ processMpeg4H263Decoder(Info, 0, numFrames, mEleStream, ostrm, inputMode));
+
+ if (mInitialized) {
+ int32_t status = PVResetVideoDecoder(mDecHandle);
+ ASSERT_EQ(status, PV_TRUE);
+ }
+
+ // Seek to next key frame and start decoding till the end
+ int32_t index = numFrames;
+ bool keyFrame = false;
+ uint32_t flags = 0;
+ while (index < (int32_t)Info.size()) {
+ if (Info[index].flags) flags = 1u << (Info[index].flags - 1);
+ if ((flags & SYNC_FRAME) == SYNC_FRAME) {
+ keyFrame = true;
+ break;
+ }
+ flags = 0;
+ mEleStream.ignore(Info[index].bytesCount);
+ index++;
+ }
+ ALOGV("Index= %d", index);
+ if (keyFrame) {
+ mNumSamplesOutput = 0;
+ ASSERT_NO_FATAL_FAILURE(processMpeg4H263Decoder(Info, index, (int32_t)Info.size() - index,
+ mEleStream, ostrm, inputMode));
+ }
+ deInitDecoder();
+ ostrm.close();
+ Info.clear();
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ Mpeg4H263DecoderTestAll, Mpeg4H263DecoderTest,
+ ::testing::Values(make_tuple("swirl_128x96_h263.h263", "swirl_128x96_h263.info", false),
+ make_tuple("swirl_176x144_h263.h263", "swirl_176x144_h263.info", false),
+ make_tuple("swirl_352x288_h263.h263", "swirl_352x288_h263.info", false),
+ make_tuple("bbb_352x288_h263.h263", "bbb_352x288_h263.info", false),
+ make_tuple("bbb_352x288_mpeg4.m4v", "bbb_352x288_mpeg4.info", true),
+ make_tuple("swirl_128x128_mpeg4.m4v", "swirl_128x128_mpeg4.info", true),
+ make_tuple("swirl_130x132_mpeg4.m4v", "swirl_130x132_mpeg4.info", true),
+ make_tuple("swirl_132x130_mpeg4.m4v", "swirl_132x130_mpeg4.info", true),
+ make_tuple("swirl_136x144_mpeg4.m4v", "swirl_136x144_mpeg4.info", true),
+ make_tuple("swirl_144x136_mpeg4.m4v", "swirl_144x136_mpeg4.info", true)));
+
+int main(int argc, char **argv) {
+ gEnv = new Mpeg4H263DecoderTestEnvironment();
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = gEnv->initFromOptions(argc, argv);
+ if (status == 0) {
+ status = RUN_ALL_TESTS();
+ ALOGD("Decoder Test Result = %d\n", status);
+ }
+ return status;
+}
diff --git a/media/libstagefright/codecs/m4v_h263/dec/test/Mpeg4H263DecoderTestEnvironment.h b/media/libstagefright/codecs/m4v_h263/dec/test/Mpeg4H263DecoderTestEnvironment.h
new file mode 100644
index 0000000..f085845
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/dec/test/Mpeg4H263DecoderTestEnvironment.h
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+#ifndef __MPEG4_H263_DECODER_TEST_ENVIRONMENT_H__
+#define __MPEG4_H263_DECODER_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class Mpeg4H263DecoderTestEnvironment : public ::testing::Environment {
+ public:
+ Mpeg4H263DecoderTestEnvironment() : res("/data/local/tmp/") {}
+
+ // Parses the command line arguments
+ int initFromOptions(int argc, char **argv);
+
+ void setRes(const char *_res) { res = _res; }
+
+ const string getRes() const { return res; }
+
+ private:
+ string res;
+};
+
+int Mpeg4H263DecoderTestEnvironment::initFromOptions(int argc, char **argv) {
+ static struct option options[] = {{"path", required_argument, 0, 'P'}, {0, 0, 0, 0}};
+
+ while (true) {
+ int index = 0;
+ int c = getopt_long(argc, argv, "P:", options, &index);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'P': {
+ setRes(optarg);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr,
+ "unrecognized option: %s\n\n"
+ "usage: %s <gtest options> <test options>\n\n"
+ "test options are:\n\n"
+ "-P, --path: Resource files directory location\n",
+ argv[optind ?: 1], argv[0]);
+ return 2;
+ }
+ return 0;
+}
+
+#endif // __MPEG4_H263_DECODER_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/codecs/m4v_h263/dec/test/README.md b/media/libstagefright/codecs/m4v_h263/dec/test/README.md
new file mode 100644
index 0000000..3f46529
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/dec/test/README.md
@@ -0,0 +1,34 @@
+## Media Testing ##
+---
+#### Mpeg4H263Decoder :
+The Mpeg4H263Decoder Test Suite validates the Mpeg4 and H263 decoder available in libstagefright.
+
+Run the following steps to build the test suite:
+```
+m Mpeg4H263DecoderTest
+```
+
+The 32-bit binaries will be created in the following path : ${OUT}/data/nativetest/
+
+The 64-bit binaries will be created in the following path : ${OUT}/data/nativetest64/
+
+To test 64-bit binary push binaries from nativetest64.
+```
+adb push ${OUT}/data/nativetest64/Mpeg4H263DecoderTest/Mpeg4H263DecoderTest /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/Mpeg4H263DecoderTest/Mpeg4H263DecoderTest /data/local/tmp/
+```
+
+The resource file for the tests is taken from [here](https://drive.google.com/drive/folders/13cM4tAaVFrmr-zGFqaAzFBbKs75pnm9b).
+Download Mpeg4H263Decoder folder and push all the files in this folder to /data/local/tmp/ on the device for testing.
+```
+adb push Mpeg4H263Decoder/. /data/local/tmp/
+```
+
+usage: Mpeg4H263DecoderTest -P \<path_to_folder\>
+```
+adb shell /data/local/tmp/Mpeg4H263DecoderTest -P /data/local/tmp/
+```
diff --git a/media/libstagefright/codecs/mp3dec/test/Android.bp b/media/libstagefright/codecs/mp3dec/test/Android.bp
new file mode 100644
index 0000000..0ff8b12
--- /dev/null
+++ b/media/libstagefright/codecs/mp3dec/test/Android.bp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+cc_test {
+ name: "Mp3DecoderTest",
+ gtest: true,
+
+ srcs: [
+ "mp3reader.cpp",
+ "Mp3DecoderTest.cpp",
+ ],
+
+ static_libs: [
+ "libstagefright_mp3dec",
+ "libsndfile",
+ "libaudioutils",
+ ],
+
+ shared_libs: [
+ "liblog",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ sanitize: {
+ cfi: true,
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ },
+}
diff --git a/media/libstagefright/codecs/mp3dec/test/Mp3DecoderTest.cpp b/media/libstagefright/codecs/mp3dec/test/Mp3DecoderTest.cpp
new file mode 100644
index 0000000..99553ec
--- /dev/null
+++ b/media/libstagefright/codecs/mp3dec/test/Mp3DecoderTest.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2019 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 "Mp3DecoderTest"
+
+#include <utils/Log.h>
+
+#include <audio_utils/sndfile.h>
+#include <stdio.h>
+
+#include "mp3reader.h"
+#include "pvmp3decoder_api.h"
+
+#include "Mp3DecoderTestEnvironment.h"
+
+#define OUTPUT_FILE "/data/local/tmp/mp3Decode.out"
+
+constexpr int32_t kInputBufferSize = 1024 * 10;
+constexpr int32_t kOutputBufferSize = 4608 * 2;
+constexpr int32_t kMaxCount = 10;
+constexpr int32_t kNumFrameReset = 150;
+
+static Mp3DecoderTestEnvironment *gEnv = nullptr;
+
+class Mp3DecoderTest : public ::testing::TestWithParam<string> {
+ public:
+ Mp3DecoderTest() : mConfig(nullptr) {}
+
+ ~Mp3DecoderTest() {
+ if (mConfig) {
+ delete mConfig;
+ mConfig = nullptr;
+ }
+ }
+
+ virtual void SetUp() override {
+ mConfig = new tPVMP3DecoderExternal{};
+ ASSERT_NE(mConfig, nullptr) << "Failed to initialize config. No Memory available";
+ mConfig->equalizerType = flat;
+ mConfig->crcEnabled = false;
+ }
+
+ tPVMP3DecoderExternal *mConfig;
+ Mp3Reader mMp3Reader;
+
+ ERROR_CODE DecodeFrames(void *decoderbuf, SNDFILE *outFileHandle, SF_INFO sfInfo,
+ int32_t frameCount = INT32_MAX);
+ SNDFILE *openOutputFile(SF_INFO *sfInfo);
+};
+
+ERROR_CODE Mp3DecoderTest::DecodeFrames(void *decoderBuf, SNDFILE *outFileHandle, SF_INFO sfInfo,
+ int32_t frameCount) {
+ uint8_t inputBuf[kInputBufferSize];
+ int16_t outputBuf[kOutputBufferSize];
+ uint32_t bytesRead;
+ ERROR_CODE decoderErr;
+ while (frameCount > 0) {
+ bool success = mMp3Reader.getFrame(inputBuf, &bytesRead);
+ if (!success) {
+ break;
+ }
+ mConfig->inputBufferCurrentLength = bytesRead;
+ mConfig->inputBufferMaxLength = 0;
+ mConfig->inputBufferUsedLength = 0;
+ mConfig->pInputBuffer = inputBuf;
+ mConfig->pOutputBuffer = outputBuf;
+ mConfig->outputFrameSize = kOutputBufferSize / sizeof(int16_t);
+ decoderErr = pvmp3_framedecoder(mConfig, decoderBuf);
+ if (decoderErr != NO_DECODING_ERROR) break;
+ sf_writef_short(outFileHandle, outputBuf, mConfig->outputFrameSize / sfInfo.channels);
+ frameCount--;
+ }
+ return decoderErr;
+}
+
+SNDFILE *Mp3DecoderTest::openOutputFile(SF_INFO *sfInfo) {
+ memset(sfInfo, 0, sizeof(SF_INFO));
+ sfInfo->channels = mMp3Reader.getNumChannels();
+ sfInfo->format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
+ sfInfo->samplerate = mMp3Reader.getSampleRate();
+ SNDFILE *outFileHandle = sf_open(OUTPUT_FILE, SFM_WRITE, sfInfo);
+ return outFileHandle;
+}
+
+TEST_F(Mp3DecoderTest, MultiCreateMp3DecoderTest) {
+ size_t memRequirements = pvmp3_decoderMemRequirements();
+ ASSERT_NE(memRequirements, 0) << "Failed to get the memory requirement size";
+ void *decoderBuf = malloc(memRequirements);
+ ASSERT_NE(decoderBuf, nullptr)
+ << "Failed to allocate decoder memory of size " << memRequirements;
+ for (int count = 0; count < kMaxCount; count++) {
+ pvmp3_InitDecoder(mConfig, decoderBuf);
+ ALOGV("Decoder created successfully");
+ }
+ if (decoderBuf) {
+ free(decoderBuf);
+ decoderBuf = nullptr;
+ }
+}
+
+TEST_P(Mp3DecoderTest, DecodeTest) {
+ size_t memRequirements = pvmp3_decoderMemRequirements();
+ ASSERT_NE(memRequirements, 0) << "Failed to get the memory requirement size";
+ void *decoderBuf = malloc(memRequirements);
+ ASSERT_NE(decoderBuf, nullptr)
+ << "Failed to allocate decoder memory of size " << memRequirements;
+
+ pvmp3_InitDecoder(mConfig, decoderBuf);
+ ALOGV("Decoder created successfully");
+ string inputFile = gEnv->getRes() + GetParam();
+ bool status = mMp3Reader.init(inputFile.c_str());
+ ASSERT_TRUE(status) << "Unable to initialize the mp3Reader";
+
+ // Open the output file.
+ SF_INFO sfInfo;
+ SNDFILE *outFileHandle = openOutputFile(&sfInfo);
+ ASSERT_NE(outFileHandle, nullptr) << "Error opening output file for writing decoded output";
+
+ ERROR_CODE decoderErr = DecodeFrames(decoderBuf, outFileHandle, sfInfo);
+ ASSERT_EQ(decoderErr, NO_DECODING_ERROR) << "Failed to decode the frames";
+ ASSERT_EQ(sfInfo.channels, mConfig->num_channels) << "Number of channels does not match";
+ ASSERT_EQ(sfInfo.samplerate, mConfig->samplingRate) << "Sample rate does not match";
+
+ mMp3Reader.close();
+ sf_close(outFileHandle);
+ if (decoderBuf) {
+ free(decoderBuf);
+ decoderBuf = nullptr;
+ }
+}
+
+TEST_P(Mp3DecoderTest, ResetDecoderTest) {
+ size_t memRequirements = pvmp3_decoderMemRequirements();
+ ASSERT_NE(memRequirements, 0) << "Failed to get the memory requirement size";
+ void *decoderBuf = malloc(memRequirements);
+ ASSERT_NE(decoderBuf, nullptr)
+ << "Failed to allocate decoder memory of size " << memRequirements;
+
+ pvmp3_InitDecoder(mConfig, decoderBuf);
+ ALOGV("Decoder created successfully.");
+ string inputFile = gEnv->getRes() + GetParam();
+ bool status = mMp3Reader.init(inputFile.c_str());
+ ASSERT_TRUE(status) << "Unable to initialize the mp3Reader";
+
+ // Open the output file.
+ SF_INFO sfInfo;
+ SNDFILE *outFileHandle = openOutputFile(&sfInfo);
+ ASSERT_NE(outFileHandle, nullptr) << "Error opening output file for writing decoded output";
+
+ ERROR_CODE decoderErr;
+ decoderErr = DecodeFrames(decoderBuf, outFileHandle, sfInfo, kNumFrameReset);
+ ASSERT_EQ(decoderErr, NO_DECODING_ERROR) << "Failed to decode the frames";
+ ASSERT_EQ(sfInfo.channels, mConfig->num_channels) << "Number of channels does not match";
+ ASSERT_EQ(sfInfo.samplerate, mConfig->samplingRate) << "Sample rate does not match";
+
+ pvmp3_resetDecoder(decoderBuf);
+ // Decode the same file.
+ decoderErr = DecodeFrames(decoderBuf, outFileHandle, sfInfo);
+ ASSERT_EQ(decoderErr, NO_DECODING_ERROR) << "Failed to decode the frames";
+ ASSERT_EQ(sfInfo.channels, mConfig->num_channels) << "Number of channels does not match";
+ ASSERT_EQ(sfInfo.samplerate, mConfig->samplingRate) << "Sample rate does not match";
+
+ mMp3Reader.close();
+ sf_close(outFileHandle);
+ if (decoderBuf) {
+ free(decoderBuf);
+ decoderBuf = nullptr;
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(Mp3DecoderTestAll, Mp3DecoderTest,
+ ::testing::Values(("bbb_44100hz_2ch_128kbps_mp3_30sec.mp3"),
+ ("bbb_44100hz_2ch_128kbps_mp3_5mins.mp3"),
+ ("bbb_mp3_stereo_192kbps_48000hz.mp3")));
+
+int main(int argc, char **argv) {
+ gEnv = new Mp3DecoderTestEnvironment();
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = gEnv->initFromOptions(argc, argv);
+ if (status == 0) {
+ status = RUN_ALL_TESTS();
+ ALOGV("Test result = %d\n", status);
+ }
+ return status;
+}
diff --git a/media/libstagefright/codecs/mp3dec/test/Mp3DecoderTestEnvironment.h b/media/libstagefright/codecs/mp3dec/test/Mp3DecoderTestEnvironment.h
new file mode 100644
index 0000000..a54b34c
--- /dev/null
+++ b/media/libstagefright/codecs/mp3dec/test/Mp3DecoderTestEnvironment.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MP3DECODER_TEST_ENVIRONMENT_H__
+#define __MP3DECODER_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class Mp3DecoderTestEnvironment : public ::testing::Environment {
+ public:
+ Mp3DecoderTestEnvironment() : res("/data/local/tmp/") {}
+
+ // Parses the command line arguments
+ int initFromOptions(int argc, char **argv);
+
+ void setRes(const char *_res) { res = _res; }
+
+ const string getRes() const { return res; }
+
+ private:
+ string res;
+};
+
+int Mp3DecoderTestEnvironment::initFromOptions(int argc, char **argv) {
+ static struct option options[] = {{"res", required_argument, 0, 'P'}, {0, 0, 0, 0}};
+
+ while (true) {
+ int index = 0;
+ int c = getopt_long(argc, argv, "P:", options, &index);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'P':
+ setRes(optarg);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr,
+ "unrecognized option: %s\n\n"
+ "usage: %s <gtest options> <test options>\n\n"
+ "test options are:\n\n"
+ "-P, --path: Resource files directory location\n",
+ argv[optind ?: 1], argv[0]);
+ return 2;
+ }
+ return 0;
+}
+
+#endif // __MP3DECODER_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/codecs/mp3dec/test/README.md b/media/libstagefright/codecs/mp3dec/test/README.md
new file mode 100644
index 0000000..019239e
--- /dev/null
+++ b/media/libstagefright/codecs/mp3dec/test/README.md
@@ -0,0 +1,34 @@
+## Media Testing ##
+---
+#### Mp3Decoder :
+The Mp3Decoder Test Suite validates the mp3decoder available in libstagefright.
+
+Run the following steps to build the test suite:
+```
+m Mp3DecoderTest
+```
+
+The 32-bit binaries will be created in the following path : ${OUT}/data/nativetest/
+
+The 64-bit binaries will be created in the following path : ${OUT}/data/nativetest64/
+
+To test 64-bit binary push binaries from nativetest64.
+```
+adb push ${OUT}/data/nativetest64/Mp3DecoderTest/Mp3DecoderTest /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/Mp3DecoderTest/Mp3DecoderTest /data/local/tmp/
+```
+
+The resource file for the tests is taken from [here](https://drive.google.com/drive/folders/13cM4tAaVFrmr-zGFqaAzFBbKs75pnm9b). Push these files into device for testing.
+Download mp3 folder and push all the files in this folder to /data/local/tmp/ on the device.
+```
+adb push mp3/. /data/local/tmp/
+```
+
+usage: Mp3DecoderTest -P \<path_to_folder\>
+```
+adb shell /data/local/tmp/Mp3DecoderTest -P /data/local/tmp/
+```
diff --git a/media/libstagefright/colorconversion/Android.bp b/media/libstagefright/colorconversion/Android.bp
index ba57497..6b08b08 100644
--- a/media/libstagefright/colorconversion/Android.bp
+++ b/media/libstagefright/colorconversion/Android.bp
@@ -15,6 +15,11 @@
"libnativewindow",
],
+ header_libs: [
+ "libstagefright_headers",
+ "libstagefright_foundation_headers",
+ ],
+
static_libs: ["libyuv_static"],
cflags: ["-Werror"],
diff --git a/media/libstagefright/flac/dec/Android.bp b/media/libstagefright/flac/dec/Android.bp
index 7ebe71f..d65a663 100644
--- a/media/libstagefright/flac/dec/Android.bp
+++ b/media/libstagefright/flac/dec/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
name: "libstagefright_flacdec",
vendor_available: true,
diff --git a/media/libstagefright/foundation/Android.bp b/media/libstagefright/foundation/Android.bp
index 5485f6d..9fe879e 100644
--- a/media/libstagefright/foundation/Android.bp
+++ b/media/libstagefright/foundation/Android.bp
@@ -2,6 +2,7 @@
name: "libstagefright_foundation_headers",
export_include_dirs: ["include"],
vendor_available: true,
+ host_supported: true,
}
cc_defaults {
@@ -34,10 +35,6 @@
"media_plugin_headers",
],
- export_shared_lib_headers: [
- "libbinder",
- ],
-
cflags: [
"-Wno-multichar",
"-Werror",
diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp
index 792a68a..425468f 100644
--- a/media/libstagefright/id3/ID3.cpp
+++ b/media/libstagefright/id3/ID3.cpp
@@ -32,7 +32,7 @@
static const size_t kMaxMetadataSize = 3 * 1024 * 1024;
-struct MemorySource : public DataSourceBase {
+struct ID3::MemorySource : public DataSourceBase {
MemorySource(const uint8_t *data, size_t size)
: mData(data),
mSize(size) {
@@ -58,7 +58,7 @@
DISALLOW_EVIL_CONSTRUCTORS(MemorySource);
};
-class DataSourceUnwrapper : public DataSourceBase {
+class ID3::DataSourceUnwrapper : public DataSourceBase {
public:
explicit DataSourceUnwrapper(DataSourceHelper *sourcehelper) {
diff --git a/media/libstagefright/id3/test/Android.bp b/media/libstagefright/id3/test/Android.bp
new file mode 100644
index 0000000..9d26eec
--- /dev/null
+++ b/media/libstagefright/id3/test/Android.bp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+cc_test {
+ name: "ID3Test",
+ gtest: true,
+
+ srcs: ["ID3Test.cpp"],
+
+ static_libs: [
+ "libdatasource",
+ "libstagefright_id3",
+ "libstagefright",
+ "libstagefright_foundation",
+ ],
+
+ shared_libs: [
+ "libutils",
+ "liblog",
+ "libbinder",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ sanitize: {
+ cfi: true,
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ },
+}
diff --git a/media/libstagefright/id3/test/ID3Test.cpp b/media/libstagefright/id3/test/ID3Test.cpp
new file mode 100644
index 0000000..a8f1470
--- /dev/null
+++ b/media/libstagefright/id3/test/ID3Test.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2019 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 "ID3Test"
+#include <utils/Log.h>
+
+#include <ctype.h>
+#include <string>
+#include <sys/stat.h>
+#include <datasource/FileSource.h>
+
+#include <media/stagefright/foundation/hexdump.h>
+#include <ID3.h>
+
+#include "ID3TestEnvironment.h"
+
+using namespace android;
+
+static ID3TestEnvironment *gEnv = nullptr;
+
+class ID3tagTest : public ::testing::TestWithParam<string> {};
+class ID3versionTest : public ::testing::TestWithParam<pair<string, int>> {};
+class ID3textTagTest : public ::testing::TestWithParam<pair<string, int>> {};
+class ID3albumArtTest : public ::testing::TestWithParam<pair<string, bool>> {};
+class ID3multiAlbumArtTest : public ::testing::TestWithParam<pair<string, int>> {};
+
+TEST_P(ID3tagTest, TagTest) {
+ string path = gEnv->getRes() + GetParam();
+ sp<FileSource> file = new FileSource(path.c_str());
+ ASSERT_EQ(file->initCheck(), (status_t)OK) << "File initialization failed! \n";
+ ID3 tag(file.get());
+ ASSERT_TRUE(tag.isValid()) << "No valid ID3 tag found for " << path.c_str() << "\n";
+
+ ID3::Iterator it(tag, nullptr);
+ while (!it.done()) {
+ String8 id;
+ it.getID(&id);
+ ASSERT_GT(id.length(), 0) << "No ID tag found! \n";
+ ALOGV("Found ID tag: %s\n", String8(id).c_str());
+ it.next();
+ }
+}
+
+TEST_P(ID3versionTest, VersionTest) {
+ int versionNumber = GetParam().second;
+ string path = gEnv->getRes() + GetParam().first;
+ sp<android::FileSource> file = new FileSource(path.c_str());
+ ASSERT_EQ(file->initCheck(), (status_t)OK) << "File initialization failed! \n";
+
+ ID3 tag(file.get());
+ ASSERT_TRUE(tag.isValid()) << "No valid ID3 tag found for " << path.c_str() << "\n";
+ ASSERT_TRUE(tag.version() >= versionNumber)
+ << "Expected version: " << tag.version() << " Found version: " << versionNumber;
+}
+
+TEST_P(ID3textTagTest, TextTagTest) {
+ int numTextFrames = GetParam().second;
+ string path = gEnv->getRes() + GetParam().first;
+ sp<android::FileSource> file = new FileSource(path.c_str());
+ ASSERT_EQ(file->initCheck(), (status_t)OK) << "File initialization failed! \n";
+
+ ID3 tag(file.get());
+ ASSERT_TRUE(tag.isValid()) << "No valid ID3 tag found for " << path.c_str() << "\n";
+ int countTextFrames = 0;
+ ID3::Iterator it(tag, nullptr);
+ while (!it.done()) {
+ String8 id;
+ it.getID(&id);
+ ASSERT_GT(id.length(), 0);
+ if (id[0] == 'T') {
+ String8 text;
+ countTextFrames++;
+ it.getString(&text);
+ ALOGV("Found text frame %s : %s \n", id.string(), text.string());
+ }
+ it.next();
+ }
+ ASSERT_EQ(countTextFrames, numTextFrames)
+ << "Expected " << numTextFrames << " text frames, found " << countTextFrames;
+}
+
+TEST_P(ID3albumArtTest, AlbumArtTest) {
+ bool albumArtPresent = GetParam().second;
+ string path = gEnv->getRes() + GetParam().first;
+ sp<android::FileSource> file = new FileSource(path.c_str());
+ ASSERT_EQ(file->initCheck(), (status_t)OK) << "File initialization failed! \n";
+
+ ID3 tag(file.get());
+ ASSERT_TRUE(tag.isValid()) << "No valid ID3 tag found for " << path.c_str() << "\n";
+ size_t dataSize;
+ String8 mime;
+ const void *data = tag.getAlbumArt(&dataSize, &mime);
+
+ if (albumArtPresent) {
+ if (data) {
+ ALOGV("Found album art: size = %zu mime = %s \n", dataSize, mime.string());
+ }
+ ASSERT_NE(data, nullptr) << "Expected album art, found none!" << path;
+ } else {
+ ASSERT_EQ(data, nullptr) << "Found album art when expected none!";
+ }
+#if (LOG_NDEBUG == 0)
+ hexdump(data, dataSize > 128 ? 128 : dataSize);
+#endif
+}
+
+TEST_P(ID3multiAlbumArtTest, MultiAlbumArtTest) {
+ int numAlbumArt = GetParam().second;
+ string path = gEnv->getRes() + GetParam().first;
+ sp<android::FileSource> file = new FileSource(path.c_str());
+ ASSERT_EQ(file->initCheck(), (status_t)OK) << "File initialization failed! \n";
+
+ ID3 tag(file.get());
+ ASSERT_TRUE(tag.isValid()) << "No valid ID3 tag found for " << path.c_str() << "\n";
+ int count = 0;
+ ID3::Iterator it(tag, nullptr);
+ while (!it.done()) {
+ String8 id;
+ it.getID(&id);
+ ASSERT_GT(id.length(), 0);
+ // Check if the tag is an "APIC/PIC" tag.
+ if (String8(id) == "APIC" || String8(id) == "PIC") {
+ count++;
+ size_t dataSize;
+ String8 mime;
+ const void *data = tag.getAlbumArt(&dataSize, &mime);
+ if (data) {
+ ALOGV("Found album art: size = %zu mime = %s \n", dataSize, mime.string());
+#if (LOG_NDEBUG == 0)
+ hexdump(data, dataSize > 128 ? 128 : dataSize);
+#endif
+ }
+ ASSERT_NE(data, nullptr) << "Expected album art, found none!" << path;
+ }
+ it.next();
+ }
+ ASSERT_EQ(count, numAlbumArt) << "Found " << count << " album arts, expected " << numAlbumArt
+ << " album arts! \n";
+}
+
+INSTANTIATE_TEST_SUITE_P(id3TestAll, ID3tagTest,
+ ::testing::Values("bbb_44100hz_2ch_128kbps_mp3_30sec.mp3",
+ "bbb_44100hz_2ch_128kbps_mp3_30sec_1_image.mp3",
+ "bbb_44100hz_2ch_128kbps_mp3_30sec_2_image.mp3",
+ "bbb_44100hz_2ch_128kbps_mp3_5mins.mp3",
+ "bbb_44100hz_2ch_128kbps_mp3_5mins_1_image.mp3",
+ "bbb_44100hz_2ch_128kbps_mp3_5mins_2_image.mp3",
+ "bbb_44100hz_2ch_128kbps_mp3_5mins_largeSize.mp3",
+ "bbb_44100hz_2ch_128kbps_mp3_30sec_moreTextFrames.mp3"));
+
+INSTANTIATE_TEST_SUITE_P(
+ id3TestAll, ID3versionTest,
+ ::testing::Values(make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec.mp3", 4),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_1_image.mp3", 4),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_2_image.mp3", 4),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins.mp3", 4),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_1_image.mp3", 4),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_2_image.mp3", 4),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_largeSize.mp3", 4),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_moreTextFrames.mp3", 4)));
+
+INSTANTIATE_TEST_SUITE_P(
+ id3TestAll, ID3textTagTest,
+ ::testing::Values(make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec.mp3", 1),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_1_image.mp3", 1),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_2_image.mp3", 1),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins.mp3", 1),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_1_image.mp3", 1),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_2_image.mp3", 1),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_largeSize.mp3", 1),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_moreTextFrames.mp3", 5)));
+
+INSTANTIATE_TEST_SUITE_P(
+ id3TestAll, ID3albumArtTest,
+ ::testing::Values(make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec.mp3", false),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_1_image.mp3", true),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_2_image.mp3", true),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins.mp3", false),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_1_image.mp3", true),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_2_image.mp3", true),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_largeSize.mp3", true)));
+
+INSTANTIATE_TEST_SUITE_P(
+ id3TestAll, ID3multiAlbumArtTest,
+ ::testing::Values(make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec.mp3", 0),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins.mp3", 0),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_1_image.mp3", 1),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_1_image.mp3", 1),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_2_image.mp3", 2),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_2_image.mp3", 2),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_largeSize.mp3", 3)));
+
+int main(int argc, char **argv) {
+ gEnv = new ID3TestEnvironment();
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = gEnv->initFromOptions(argc, argv);
+ if (status == 0) {
+ status = RUN_ALL_TESTS();
+ ALOGI("ID3 Test result = %d\n", status);
+ }
+ return status;
+}
diff --git a/media/libstagefright/id3/test/ID3TestEnvironment.h b/media/libstagefright/id3/test/ID3TestEnvironment.h
new file mode 100644
index 0000000..2229718
--- /dev/null
+++ b/media/libstagefright/id3/test/ID3TestEnvironment.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __ID3_TEST_ENVIRONMENT_H__
+#define __ID3_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class ID3TestEnvironment : public::testing::Environment {
+ public:
+ ID3TestEnvironment() : res("/data/local/tmp/") {}
+
+ // Parses the command line arguments
+ int initFromOptions(int argc, char **argv);
+
+ void setRes(const char *_res) { res = _res; }
+
+ const string getRes() const { return res; }
+
+ private:
+ string res;
+};
+
+int ID3TestEnvironment::initFromOptions(int argc, char **argv) {
+ static struct option options[] = {{"path", required_argument, 0, 'P'}, {0, 0, 0, 0}};
+
+ while (true) {
+ int index = 0;
+ int c = getopt_long(argc, argv, "P:", options, &index);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'P': {
+ setRes(optarg);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr,
+ "unrecognized option: %s\n\n"
+ "usage: %s <gtest options> <test options>\n\n"
+ "test options are:\n\n"
+ "-P, --path: Resource files directory location\n",
+ argv[optind ?: 1], argv[0]);
+ return 2;
+ }
+ return 0;
+}
+
+#endif // __ID3_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/id3/test/README.md b/media/libstagefright/id3/test/README.md
new file mode 100644
index 0000000..72f730c
--- /dev/null
+++ b/media/libstagefright/id3/test/README.md
@@ -0,0 +1,34 @@
+## Media Testing ##
+---
+#### ID3 Test :
+The ID3 Test Suite validates the ID3 parser available in libstagefright.
+
+Run the following command in the id3 folder to build the test suite:
+```
+m ID3Test
+```
+
+The 32-bit binaries will be created in the following path : ${OUT}/data/nativetest/
+
+The 64-bit binaries will be created in the following path : ${OUT}/data/nativetest64/
+
+To test 64-bit binary push binaries from nativetest64.
+```
+adb push ${OUT}/data/nativetest64/ID3Test/ID3Test /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/ID3Test/ID3Test /data/local/tmp/
+```
+
+The resource file for the tests is taken from [here](https://drive.google.com/drive/folders/1pt5HFVSysbqfyqY1sVJ9MTupZKCdqjYZ). Push these files into device for testing.
+Download ID3 folder and push all the files in this folder to /data/local/tmp/ID3 on the device.
+```
+adb push ID3/. /data/local/tmp/ID3
+```
+
+usage: ID3Test -P \<path_to_folder\>
+```
+adb shell /data/local/tmp/ID3Test -P /data/local/tmp/ID3/
+```
diff --git a/media/libstagefright/include/ACodecBufferChannel.h b/media/libstagefright/include/ACodecBufferChannel.h
index 5f65575..da962d1 100644
--- a/media/libstagefright/include/ACodecBufferChannel.h
+++ b/media/libstagefright/include/ACodecBufferChannel.h
@@ -81,6 +81,20 @@
const CryptoPlugin::SubSample *subSamples,
size_t numSubSamples,
AString *errorDetailMsg) override;
+ virtual status_t attachBuffer(
+ const std::shared_ptr<C2Buffer> &c2Buffer,
+ const sp<MediaCodecBuffer> &buffer) override;
+ virtual status_t attachEncryptedBuffer(
+ const sp<hardware::HidlMemory> &memory,
+ bool secure,
+ const uint8_t *key,
+ const uint8_t *iv,
+ CryptoPlugin::Mode mode,
+ CryptoPlugin::Pattern pattern,
+ size_t offset,
+ const CryptoPlugin::SubSample *subSamples,
+ size_t numSubSamples,
+ const sp<MediaCodecBuffer> &buffer) override;
virtual status_t renderOutputBuffer(
const sp<MediaCodecBuffer> &buffer, int64_t timestampNs) override;
virtual status_t discardBuffer(const sp<MediaCodecBuffer> &buffer) override;
@@ -118,12 +132,15 @@
void drainThisBuffer(IOMX::buffer_id bufferID, OMX_U32 omxFlags);
private:
+ int32_t getHeapSeqNum(const sp<HidlMemory> &memory);
+
const sp<AMessage> mInputBufferFilled;
const sp<AMessage> mOutputBufferDrained;
sp<MemoryDealer> mDealer;
sp<IMemory> mDecryptDestination;
int32_t mHeapSeqNum;
+ std::map<wp<HidlMemory>, int32_t> mHeapSeqNumMap;
sp<HidlMemory> mHidlMemory;
// These should only be accessed via std::atomic_* functions.
diff --git a/media/libstagefright/include/FrameDecoder.h b/media/libstagefright/include/FrameDecoder.h
index 353c957..19ae0e3 100644
--- a/media/libstagefright/include/FrameDecoder.h
+++ b/media/libstagefright/include/FrameDecoder.h
@@ -22,7 +22,7 @@
#include <media/stagefright/foundation/AString.h>
#include <media/stagefright/foundation/ABase.h>
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/openmax/OMX_Video.h>
#include <ui/GraphicTypes.h>
diff --git a/media/libstagefright/include/ID3.h b/media/libstagefright/include/ID3.h
index 5e433ea..2843a7a 100644
--- a/media/libstagefright/include/ID3.h
+++ b/media/libstagefright/include/ID3.h
@@ -77,6 +77,8 @@
size_t rawSize() const { return mRawSize; }
private:
+ class DataSourceUnwrapper;
+ struct MemorySource;
bool mIsValid;
uint8_t *mData;
size_t mSize;
diff --git a/media/libstagefright/include/media/stagefright/AACWriter.h b/media/libstagefright/include/media/stagefright/AACWriter.h
index 7c63ddd..2671138 100644
--- a/media/libstagefright/include/media/stagefright/AACWriter.h
+++ b/media/libstagefright/include/media/stagefright/AACWriter.h
@@ -17,7 +17,7 @@
#ifndef AAC_WRITER_H_
#define AAC_WRITER_H_
-#include "foundation/ABase.h"
+#include "media/stagefright/foundation/ABase.h"
#include <media/stagefright/MediaWriter.h>
#include <utils/threads.h>
diff --git a/media/libstagefright/include/media/stagefright/ACodec.h b/media/libstagefright/include/media/stagefright/ACodec.h
index 7754de4..d56ec4f 100644
--- a/media/libstagefright/include/media/stagefright/ACodec.h
+++ b/media/libstagefright/include/media/stagefright/ACodec.h
@@ -158,6 +158,7 @@
kFlagIsSecure = 1,
kFlagPushBlankBuffersToNativeWindowOnShutdown = 2,
kFlagIsGrallocUsageProtected = 4,
+ kFlagPreregisterMetadataBuffers = 8,
};
enum {
diff --git a/media/libstagefright/include/media/stagefright/AudioSource.h b/media/libstagefright/include/media/stagefright/AudioSource.h
index ad4ab47..451aa57 100644
--- a/media/libstagefright/include/media/stagefright/AudioSource.h
+++ b/media/libstagefright/include/media/stagefright/AudioSource.h
@@ -20,7 +20,7 @@
#include <media/AudioRecord.h>
#include <media/AudioSystem.h>
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/MicrophoneInfo.h>
#include <media/stagefright/MediaBuffer.h>
#include <utils/List.h>
diff --git a/media/libstagefright/include/media/stagefright/CallbackMediaSource.h b/media/libstagefright/include/media/stagefright/CallbackMediaSource.h
index 33453fa..d2adbb9 100644
--- a/media/libstagefright/include/media/stagefright/CallbackMediaSource.h
+++ b/media/libstagefright/include/media/stagefright/CallbackMediaSource.h
@@ -17,7 +17,7 @@
#ifndef CALLBACK_MEDIA_SOURCE_H_
#define CALLBACK_MEDIA_SOURCE_H_
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/stagefright/foundation/ABase.h>
namespace android {
diff --git a/media/libstagefright/include/media/stagefright/CameraSource.h b/media/libstagefright/include/media/stagefright/CameraSource.h
index 3037b72..6f0d3b5 100644
--- a/media/libstagefright/include/media/stagefright/CameraSource.h
+++ b/media/libstagefright/include/media/stagefright/CameraSource.h
@@ -19,7 +19,7 @@
#define CAMERA_SOURCE_H_
#include <deque>
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MediaBuffer.h>
#include <camera/android/hardware/ICamera.h>
#include <camera/ICameraRecordingProxy.h>
diff --git a/media/libstagefright/include/media/stagefright/CodecBase.h b/media/libstagefright/include/media/stagefright/CodecBase.h
index bc7881c..dd6df90 100644
--- a/media/libstagefright/include/media/stagefright/CodecBase.h
+++ b/media/libstagefright/include/media/stagefright/CodecBase.h
@@ -34,6 +34,8 @@
#include <system/graphics.h>
#include <utils/NativeHandle.h>
+class C2Buffer;
+
namespace android {
class BufferChannelBase;
struct BufferProducerWrapper;
@@ -45,6 +47,7 @@
class IMemory;
namespace hardware {
+class HidlMemory;
namespace cas {
namespace native {
namespace V1_0 {
@@ -295,6 +298,50 @@
size_t numSubSamples,
AString *errorDetailMsg) = 0;
/**
+ * Attach a Codec 2.0 buffer to MediaCodecBuffer.
+ *
+ * @return OK if successful;
+ * -ENOENT if index is not recognized
+ * -ENOSYS if attaching buffer is not possible or not supported
+ */
+ virtual status_t attachBuffer(
+ const std::shared_ptr<C2Buffer> &c2Buffer,
+ const sp<MediaCodecBuffer> &buffer) {
+ (void)c2Buffer;
+ (void)buffer;
+ return -ENOSYS;
+ }
+ /**
+ * Attach an encrypted HidlMemory buffer to an index
+ *
+ * @return OK if successful;
+ * -ENOENT if index is not recognized
+ * -ENOSYS if attaching buffer is not possible or not supported
+ */
+ virtual status_t attachEncryptedBuffer(
+ const sp<hardware::HidlMemory> &memory,
+ bool secure,
+ const uint8_t *key,
+ const uint8_t *iv,
+ CryptoPlugin::Mode mode,
+ CryptoPlugin::Pattern pattern,
+ size_t offset,
+ const CryptoPlugin::SubSample *subSamples,
+ size_t numSubSamples,
+ const sp<MediaCodecBuffer> &buffer) {
+ (void)memory;
+ (void)secure;
+ (void)key;
+ (void)iv;
+ (void)mode;
+ (void)pattern;
+ (void)offset;
+ (void)subSamples;
+ (void)numSubSamples;
+ (void)buffer;
+ return -ENOSYS;
+ }
+ /**
* Request buffer rendering at specified time.
*
* @param timestampNs nanosecond timestamp for rendering time.
diff --git a/media/libstagefright/include/media/stagefright/InterfaceUtils.h b/media/libstagefright/include/media/stagefright/InterfaceUtils.h
index 671f2ff..92ef543 100644
--- a/media/libstagefright/include/media/stagefright/InterfaceUtils.h
+++ b/media/libstagefright/include/media/stagefright/InterfaceUtils.h
@@ -19,7 +19,7 @@
#include <utils/RefBase.h>
#include <media/stagefright/RemoteMediaExtractor.h>
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <android/IMediaExtractor.h>
#include <media/IMediaSource.h>
diff --git a/media/libstagefright/include/media/stagefright/JPEGSource.h b/media/libstagefright/include/media/stagefright/JPEGSource.h
index 8ab3d11..53cb344 100644
--- a/media/libstagefright/include/media/stagefright/JPEGSource.h
+++ b/media/libstagefright/include/media/stagefright/JPEGSource.h
@@ -18,7 +18,7 @@
#define JPEG_SOURCE_H_
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
namespace android {
diff --git a/media/libstagefright/include/media/stagefright/MPEG4Writer.h b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
index 6f19023..34a7d55 100644
--- a/media/libstagefright/include/media/stagefright/MPEG4Writer.h
+++ b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
@@ -23,8 +23,10 @@
#include <media/stagefright/MediaWriter.h>
#include <utils/List.h>
#include <utils/threads.h>
+#include <map>
#include <media/stagefright/foundation/AHandlerReflector.h>
#include <media/stagefright/foundation/ALooper.h>
+#include <mutex>
namespace android {
@@ -58,6 +60,10 @@
void writeFourcc(const char *fourcc);
void write(const void *data, size_t size);
inline size_t write(const void *ptr, size_t size, size_t nmemb);
+ // Write to file system by calling ::write() or post error message to looper on failure.
+ void writeOrPostError(int fd, const void *buf, size_t count);
+ // Seek in the file by calling ::lseek64() or post error message to looper on failure.
+ void seekOrPostError(int fd, off64_t offset, int whence);
void endBox();
uint32_t interleaveDuration() const { return mInterleaveDurationUs; }
status_t setInterleaveDuration(uint32_t duration);
@@ -80,6 +86,8 @@
enum {
kWhatSwitch = 'swch',
+ kWhatHandleIOError = 'ioer',
+ kWhatHandleFallocateError = 'faer'
};
int mFd;
@@ -88,14 +96,15 @@
status_t mInitCheck;
bool mIsRealTimeRecording;
bool mUse4ByteNalLength;
- bool mUse32BitOffset;
bool mIsFileSizeLimitExplicitlyRequested;
bool mPaused;
bool mStarted; // Writer thread + track threads started successfully
bool mWriterThreadStarted; // Only writer thread started successfully
bool mSendNotify;
off64_t mOffset;
- off_t mMdatOffset;
+ off64_t mPreAllocateFileEndOffset; //End of file offset during preallocation.
+ off64_t mMdatOffset;
+ off64_t mMdatEndOffset; // End offset of mdat atom.
uint8_t *mInMemoryCache;
off64_t mInMemoryCacheOffset;
off64_t mInMemoryCacheSize;
@@ -106,17 +115,24 @@
uint32_t mInterleaveDurationUs;
int32_t mTimeScale;
int64_t mStartTimestampUs;
- int32_t mStartTimeOffsetBFramesUs; // Start time offset when B Frames are present
+ int32_t mStartTimeOffsetBFramesUs; // Longest offset needed for reordering tracks with B Frames
int mLatitudex10000;
int mLongitudex10000;
bool mAreGeoTagsAvailable;
int32_t mStartTimeOffsetMs;
bool mSwitchPending;
+ bool mWriteSeekErr;
+ bool mFallocateErr;
+ bool mPreAllocationEnabled;
sp<ALooper> mLooper;
sp<AHandlerReflector<MPEG4Writer> > mReflector;
Mutex mLock;
+ std::mutex mResetMutex;
+ std::mutex mFallocMutex;
+ bool mPreAllocFirstTime; // Pre-allocate space for file and track headers only once per file.
+ uint64_t mPrevAllTracksTotalMetaDataSizeEstimate;
List<Track *> mTracks;
@@ -200,19 +216,23 @@
} ItemProperty;
bool mHasFileLevelMeta;
+ uint64_t mFileLevelMetaDataSize;
bool mHasMoovBox;
uint32_t mPrimaryItemId;
uint32_t mAssociationEntryCount;
uint32_t mNumGrids;
+ uint16_t mNextItemId;
bool mHasRefs;
- Vector<ItemInfo> mItems;
+ std::map<uint32_t, ItemInfo> mItems;
Vector<ItemProperty> mProperties;
// Writer thread handling
status_t startWriterThread();
- void stopWriterThread();
+ status_t stopWriterThread();
static void *ThreadWrapper(void *me);
void threadFunc();
+ void setupAndStartLooper();
+ void stopAndReleaseLooper();
// Buffer a single chunk to be written out later.
void bufferChunk(const Chunk& chunk);
@@ -259,11 +279,11 @@
void addLengthPrefixedSample_l(MediaBuffer *buffer);
void addMultipleLengthPrefixedSamples_l(MediaBuffer *buffer);
uint16_t addProperty_l(const ItemProperty &);
+ status_t reserveItemId_l(size_t numItems, uint16_t *itemIdBase);
uint16_t addItem_l(const ItemInfo &);
void addRefs_l(uint16_t itemId, const ItemRefs &);
bool exceedsFileSizeLimit();
- bool use32BitFileOffset() const;
bool exceedsFileDurationLimit();
bool approachingFileSizeLimit();
bool isFileStreamable() const;
@@ -284,6 +304,16 @@
void writeIlst();
void writeMoovLevelMetaBox();
+ /*
+ * Allocate space needed for MOOV atom in advance and maintain just enough before write
+ * of any data. Stop writing and save MOOV atom if there was any error.
+ */
+ bool preAllocate(uint64_t wantSize);
+ /*
+ * Truncate file as per the size used for meta data and actual data in a session.
+ */
+ bool truncatePreAllocation();
+
// HEIF writing
void writeIlocBox();
void writeInfeBox(uint16_t itemId, const char *type, uint32_t flags);
diff --git a/media/libstagefright/include/media/stagefright/MediaAdapter.h b/media/libstagefright/include/media/stagefright/MediaAdapter.h
index 589c827..177a9e9 100644
--- a/media/libstagefright/include/media/stagefright/MediaAdapter.h
+++ b/media/libstagefright/include/media/stagefright/MediaAdapter.h
@@ -17,7 +17,7 @@
#ifndef MEDIA_ADAPTER_H
#define MEDIA_ADAPTER_H
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MetaData.h>
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index 78cb01c..022c48e 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -29,6 +29,10 @@
#include <media/stagefright/FrameRenderTracker.h>
#include <utils/Vector.h>
+class C2Buffer;
+class C2GraphicBlock;
+class C2LinearBlock;
+
namespace aidl {
namespace android {
namespace media {
@@ -65,7 +69,8 @@
struct MediaCodec : public AHandler {
enum ConfigureFlags {
- CONFIGURE_FLAG_ENCODE = 1,
+ CONFIGURE_FLAG_ENCODE = 1,
+ CONFIGURE_FLAG_USE_BLOCK_MODEL = 2,
};
enum BufferFlags {
@@ -157,6 +162,38 @@
uint32_t flags,
AString *errorDetailMsg = NULL);
+ status_t queueBuffer(
+ size_t index,
+ const std::shared_ptr<C2Buffer> &buffer,
+ int64_t presentationTimeUs,
+ uint32_t flags,
+ const sp<AMessage> &tunings,
+ AString *errorDetailMsg = NULL);
+
+ status_t queueEncryptedBuffer(
+ size_t index,
+ const sp<hardware::HidlMemory> &memory,
+ size_t offset,
+ const CryptoPlugin::SubSample *subSamples,
+ size_t numSubSamples,
+ const uint8_t key[16],
+ const uint8_t iv[16],
+ CryptoPlugin::Mode mode,
+ const CryptoPlugin::Pattern &pattern,
+ int64_t presentationTimeUs,
+ uint32_t flags,
+ const sp<AMessage> &tunings,
+ AString *errorDetailMsg = NULL);
+
+ std::shared_ptr<C2Buffer> decrypt(
+ const std::shared_ptr<C2Buffer> &buffer,
+ const CryptoPlugin::SubSample *subSamples,
+ size_t numSubSamples,
+ const uint8_t key[16],
+ const uint8_t iv[16],
+ CryptoPlugin::Mode mode,
+ const CryptoPlugin::Pattern &pattern);
+
status_t dequeueInputBuffer(size_t *index, int64_t timeoutUs = 0ll);
status_t dequeueOutputBuffer(
@@ -206,6 +243,29 @@
static size_t CreateFramesRenderedMessage(
const std::list<FrameRenderTracker::Info> &done, sp<AMessage> &msg);
+ static status_t CanFetchLinearBlock(
+ const std::vector<std::string> &names, bool *isCompatible);
+
+ static std::shared_ptr<C2LinearBlock> FetchLinearBlock(
+ size_t capacity, const std::vector<std::string> &names);
+
+ static status_t CanFetchGraphicBlock(
+ const std::vector<std::string> &names, bool *isCompatible);
+
+ static std::shared_ptr<C2GraphicBlock> FetchGraphicBlock(
+ int32_t width,
+ int32_t height,
+ int32_t format,
+ uint64_t usage,
+ const std::vector<std::string> &names);
+
+ template <typename T>
+ struct WrapperObject : public RefBase {
+ WrapperObject(const T& v) : value(v) {}
+ WrapperObject(T&& v) : value(std::move(v)) {}
+ T value;
+ };
+
protected:
virtual ~MediaCodec();
virtual void onMessageReceived(const sp<AMessage> &msg);
@@ -282,6 +342,7 @@
kFlagIsAsync = 1024,
kFlagIsComponentAllocated = 2048,
kFlagPushBlankBuffersOnShutdown = 4096,
+ kFlagUseBlockModel = 8192,
};
struct BufferInfo {
@@ -313,6 +374,7 @@
void updateMediametrics();
void flushMediametrics();
void updateEphemeralMediametrics(mediametrics_handle_t item);
+ void updateLowLatency(const sp<AMessage> &msg);
sp<AMessage> mOutputFormat;
sp<AMessage> mInputFormat;
@@ -440,6 +502,12 @@
std::deque<BufferFlightTiming_t> mBuffersInFlight;
Mutex mLatencyLock;
int64_t mLatencyUnknown; // buffers for which we couldn't calculate latency
+ int64_t mNumLowLatencyEnables; // how many times low latency mode is enabled
+ int64_t mNumLowLatencyDisables; // how many times low latency mode is disabled
+ bool mIsLowLatencyModeOn; // is low latency mode on currently
+ int64_t mIndexOfFirstFrameWhenLowLatencyOn; // index of the first frame queued
+ // when low latency is on
+ int64_t mInputBufferCounter; // number of input buffers queued since last reset/flush
sp<BatteryChecker> mBatteryChecker;
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
index 16e207d..eb7e5f7 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
@@ -830,6 +830,7 @@
constexpr int32_t BUFFER_FLAG_PARTIAL_FRAME = 8;
constexpr int32_t BUFFER_FLAG_SYNC_FRAME = 1;
constexpr int32_t CONFIGURE_FLAG_ENCODE = 1;
+constexpr int32_t CONFIGURE_FLAG_USE_BLOCK_MODEL = 2;
constexpr int32_t CRYPTO_MODE_AES_CBC = 2;
constexpr int32_t CRYPTO_MODE_AES_CTR = 1;
constexpr int32_t CRYPTO_MODE_UNENCRYPTED = 0;
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecSource.h b/media/libstagefright/include/media/stagefright/MediaCodecSource.h
index a68cc19..2f98af1 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecSource.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecSource.h
@@ -17,7 +17,7 @@
#ifndef MediaCodecSource_H_
#define MediaCodecSource_H_
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/foundation/AHandlerReflector.h>
#include <media/stagefright/foundation/Mutexed.h>
diff --git a/media/libstagefright/include/media/stagefright/MediaErrors.h b/media/libstagefright/include/media/stagefright/MediaErrors.h
index 6f48c5d..5418f10 100644
--- a/media/libstagefright/include/media/stagefright/MediaErrors.h
+++ b/media/libstagefright/include/media/stagefright/MediaErrors.h
@@ -105,7 +105,8 @@
ERROR_CAS_CARD_MUTE = CAS_ERROR_BASE - 15,
ERROR_CAS_CARD_INVALID = CAS_ERROR_BASE - 16,
ERROR_CAS_BLACKOUT = CAS_ERROR_BASE - 17,
- ERROR_CAS_LAST_USED_ERRORCODE = CAS_ERROR_BASE - 17,
+ ERROR_CAS_REBOOTING = CAS_ERROR_BASE - 18,
+ ERROR_CAS_LAST_USED_ERRORCODE = CAS_ERROR_BASE - 18,
ERROR_CAS_VENDOR_MAX = CAS_ERROR_BASE - 500,
ERROR_CAS_VENDOR_MIN = CAS_ERROR_BASE - 999,
diff --git a/media/libstagefright/include/media/stagefright/MediaMuxer.h b/media/libstagefright/include/media/stagefright/MediaMuxer.h
index 69d6cde..7c75f74 100644
--- a/media/libstagefright/include/media/stagefright/MediaMuxer.h
+++ b/media/libstagefright/include/media/stagefright/MediaMuxer.h
@@ -22,7 +22,7 @@
#include <utils/Vector.h>
#include <utils/threads.h>
-#include "foundation/ABase.h"
+#include "media/stagefright/foundation/ABase.h"
namespace android {
@@ -117,21 +117,24 @@
status_t writeSampleData(const sp<ABuffer> &buffer, size_t trackIndex,
int64_t timeUs, uint32_t flags) ;
+ void notify(int msg, int ext1, int ext2);
+
private:
const OutputFormat mFormat;
sp<MediaWriter> mWriter;
Vector< sp<MediaAdapter> > mTrackList; // Each track has its MediaAdapter.
sp<MetaData> mFileMeta; // Metadata for the whole file.
-
Mutex mMuxerLock;
enum State {
UNINITIALIZED,
INITIALIZED,
STARTED,
- STOPPED
+ STOPPED,
+ ERROR
};
State mState;
+ status_t mError;
DISALLOW_EVIL_CONSTRUCTORS(MediaMuxer);
};
diff --git a/media/libstagefright/include/media/stagefright/MediaWriter.h b/media/libstagefright/include/media/stagefright/MediaWriter.h
index 972ae1d..08e54b3 100644
--- a/media/libstagefright/include/media/stagefright/MediaWriter.h
+++ b/media/libstagefright/include/media/stagefright/MediaWriter.h
@@ -19,8 +19,9 @@
#define MEDIA_WRITER_H_
#include <utils/RefBase.h>
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/IMediaRecorderClient.h>
+#include <media/stagefright/MediaMuxer.h>
namespace android {
@@ -36,7 +37,7 @@
virtual status_t stop() = 0;
virtual status_t pause() = 0;
virtual status_t setCaptureRate(float /* captureFps */) {
- ALOGW("setCaptureRate unsupported");
+ ALOG(LOG_WARN, "MediaWriter", "setCaptureRate unsupported");
return ERROR_UNSUPPORTED;
}
@@ -45,6 +46,7 @@
virtual void setListener(const sp<IMediaRecorderClient>& listener) {
mListener = listener;
}
+ virtual void setMuxerListener(const wp<MediaMuxer>& muxer) { mMuxer = muxer; }
virtual status_t dump(int /*fd*/, const Vector<String16>& /*args*/) {
return OK;
@@ -59,11 +61,17 @@
int64_t mMaxFileSizeLimitBytes;
int64_t mMaxFileDurationLimitUs;
sp<IMediaRecorderClient> mListener;
+ wp<MediaMuxer> mMuxer;
void notify(int msg, int ext1, int ext2) {
- if (mListener != NULL) {
+ ALOG(LOG_VERBOSE, "MediaWriter", "notify msg:%d, ext1:%d, ext2:%d", msg, ext1, ext2);
+ if (mListener != nullptr) {
mListener->notify(msg, ext1, ext2);
}
+ sp<MediaMuxer> muxer = mMuxer.promote();
+ if (muxer != nullptr) {
+ muxer->notify(msg, ext1, ext2);
+ }
}
private:
MediaWriter(const MediaWriter &);
diff --git a/media/libstagefright/include/media/stagefright/MetaDataBase.h b/media/libstagefright/include/media/stagefright/MetaDataBase.h
index e17093a..b9cd18a 100644
--- a/media/libstagefright/include/media/stagefright/MetaDataBase.h
+++ b/media/libstagefright/include/media/stagefright/MetaDataBase.h
@@ -113,8 +113,6 @@
kKeyVideoProfile = 'vprf', // int32_t
kKeyVideoLevel = 'vlev', // int32_t
- // Set this key to enable authoring files in 64-bit offset
- kKey64BitFileOffset = 'fobt', // int32_t (bool)
kKey2ByteNalLength = '2NAL', // int32_t (bool)
// Identify the file output format for authoring
diff --git a/media/libstagefright/include/media/stagefright/NuMediaExtractor.h b/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
index 98e4b22..227cead 100644
--- a/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
+++ b/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
@@ -22,7 +22,7 @@
#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/foundation/AudioPresentationInfo.h>
#include <android/IMediaExtractor.h>
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
#include <utils/RefBase.h>
diff --git a/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h b/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h
index 2bf5a8b..2ce7bc7 100644
--- a/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h
+++ b/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h
@@ -18,12 +18,12 @@
#define REMOTE_MEDIA_EXTRACTOR_H_
#include <android/IMediaExtractor.h>
+#include <media/MediaMetricsItem.h>
#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/foundation/ABase.h>
namespace android {
-class MediaAnalyticsItem;
// IMediaExtractor wrapper to the MediaExtractor.
class RemoteMediaExtractor : public BnMediaExtractor {
@@ -41,14 +41,14 @@
virtual status_t getMetrics(Parcel *reply);
virtual uint32_t flags() const;
virtual status_t setMediaCas(const HInterfaceToken &casToken);
- virtual const char * name();
+ virtual String8 name();
private:
MediaExtractor *mExtractor;
sp<DataSource> mSource;
sp<RefBase> mExtractorPlugin;
- MediaAnalyticsItem *mAnalyticsItem;
+ mediametrics::Item *mMetricsItem;
explicit RemoteMediaExtractor(
MediaExtractor *extractor,
diff --git a/media/libstagefright/include/media/stagefright/RemoteMediaSource.h b/media/libstagefright/include/media/stagefright/RemoteMediaSource.h
index 03d3869..2cd23f0 100644
--- a/media/libstagefright/include/media/stagefright/RemoteMediaSource.h
+++ b/media/libstagefright/include/media/stagefright/RemoteMediaSource.h
@@ -18,7 +18,7 @@
#define REMOTE_MEDIA_SOURCE_H_
#include <media/IMediaSource.h>
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/stagefright/foundation/ABase.h>
namespace android {
diff --git a/media/libstagefright/include/media/stagefright/SimpleDecodingSource.h b/media/libstagefright/include/media/stagefright/SimpleDecodingSource.h
index 23defb4..a97ae23 100644
--- a/media/libstagefright/include/media/stagefright/SimpleDecodingSource.h
+++ b/media/libstagefright/include/media/stagefright/SimpleDecodingSource.h
@@ -17,7 +17,7 @@
#ifndef SIMPLE_DECODING_SOURCE_H_
#define SIMPLE_DECODING_SOURCE_H_
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/stagefright/foundation/AString.h>
#include <media/stagefright/foundation/Mutexed.h>
diff --git a/media/libstagefright/include/media/stagefright/foundation b/media/libstagefright/include/media/stagefright/foundation
deleted file mode 120000
index b9fd3b3..0000000
--- a/media/libstagefright/include/media/stagefright/foundation
+++ /dev/null
@@ -1 +0,0 @@
-../../../foundation/include/media/stagefright/foundation/
\ No newline at end of file
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index 0ff2d7e..49578d3 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -20,7 +20,7 @@
#include <sys/types.h>
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/AudioPresentationInfo.h>
diff --git a/media/libstagefright/mpeg2ts/Android.bp b/media/libstagefright/mpeg2ts/Android.bp
index a507b91..42afea3 100644
--- a/media/libstagefright/mpeg2ts/Android.bp
+++ b/media/libstagefright/mpeg2ts/Android.bp
@@ -29,7 +29,6 @@
shared_libs: [
"libcrypto",
- "libmedia",
"libhidlmemory",
"android.hardware.cas.native@1.0",
"android.hidl.memory@1.0",
@@ -37,9 +36,13 @@
],
header_libs: [
+ "libmedia_headers",
+ "libaudioclient_headers",
"media_ndk_headers",
],
+ export_include_dirs: ["."],
+
whole_static_libs: [
"libstagefright_metadatautils",
],
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
index f4a6acb..44fe2c8 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
@@ -18,7 +18,7 @@
#define ANOTHER_PACKET_SOURCE_H_
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/stagefright/foundation/ABase.h>
#include <utils/threads.h>
#include <utils/List.h>
diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp
index 7d612b4..78b4f19 100644
--- a/media/libstagefright/omx/Android.bp
+++ b/media/libstagefright/omx/Android.bp
@@ -4,6 +4,7 @@
vndk: {
enabled: true,
},
+ double_loadable: true,
srcs: [
"OMXMaster.cpp",
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index e1c3916..2f69f45 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -492,6 +492,12 @@
}
case OMX_StateLoaded:
+ {
+ if (mActiveBuffers.size() > 0) {
+ freeActiveBuffers();
+ }
+ FALLTHROUGH_INTENDED;
+ }
case OMX_StateInvalid:
break;
diff --git a/media/libstagefright/omx/tests/Android.bp b/media/libstagefright/omx/tests/Android.bp
index eb01543..fefb3bb 100644
--- a/media/libstagefright/omx/tests/Android.bp
+++ b/media/libstagefright/omx/tests/Android.bp
@@ -23,6 +23,7 @@
header_libs: [
"libbase_headers",
+ "libmediametrics_headers",
"media_ndk_headers",
],
diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp
index 6848a83..7893148 100644
--- a/media/libstagefright/omx/tests/OMXHarness.cpp
+++ b/media/libstagefright/omx/tests/OMXHarness.cpp
@@ -30,7 +30,7 @@
#include <datasource/DataSourceFactory.h>
#include <media/DataSource.h>
#include <media/IMediaHTTPService.h>
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/OMXBuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/ALooper.h>
diff --git a/media/libstagefright/rtsp/ARTPWriter.cpp b/media/libstagefright/rtsp/ARTPWriter.cpp
index 4f86773..58d6086 100644
--- a/media/libstagefright/rtsp/ARTPWriter.cpp
+++ b/media/libstagefright/rtsp/ARTPWriter.cpp
@@ -22,7 +22,7 @@
#include <fcntl.h>
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
diff --git a/media/libstagefright/rtsp/VideoSource.h b/media/libstagefright/rtsp/VideoSource.h
index 4be9bf6..f29db57 100644
--- a/media/libstagefright/rtsp/VideoSource.h
+++ b/media/libstagefright/rtsp/VideoSource.h
@@ -18,7 +18,7 @@
#define VIDEO_SOURCE_H_
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
diff --git a/media/libstagefright/tests/writer/README.md b/media/libstagefright/tests/writer/README.md
index 52db6f0..ae07917 100644
--- a/media/libstagefright/tests/writer/README.md
+++ b/media/libstagefright/tests/writer/README.md
@@ -19,12 +19,13 @@
adb push ${OUT}/data/nativetest/writerTest/writerTest /data/local/tmp/
-The resource file for the tests is taken from Codec2 VTS resource folder. Push these files into device for testing.
+The resource file for the tests is taken from [here](https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/tests/writer/writerTestRes.zip).
+Download and extract the folder. Push all the files in this folder to /data/local/tmp/ on the device.
```
-adb push $ANDROID_BUILD_TOP/frameworks/av/media/codec2/hidl/1.0/vts/functional/res /sdcard/
+adb push writerTestRes /data/local/tmp/
```
usage: writerTest -P \<path_to_res_folder\>
```
-adb shell /data/local/tmp/writerTest -P /sdcard/res/
+adb shell /data/local/tmp/writerTest -P /data/local/tmp/
```
diff --git a/media/libstagefright/tests/writer/WriterTest.cpp b/media/libstagefright/tests/writer/WriterTest.cpp
index d68438c..409f141 100644
--- a/media/libstagefright/tests/writer/WriterTest.cpp
+++ b/media/libstagefright/tests/writer/WriterTest.cpp
@@ -29,9 +29,9 @@
#include <media/stagefright/AACWriter.h>
#include <media/stagefright/AMRWriter.h>
-#include <media/stagefright/OggWriter.h>
-#include <media/stagefright/MPEG4Writer.h>
#include <media/stagefright/MPEG2TSWriter.h>
+#include <media/stagefright/MPEG4Writer.h>
+#include <media/stagefright/OggWriter.h>
#include <webm/WebmWriter.h>
#include "WriterTestEnvironment.h"
@@ -89,19 +89,36 @@
class WriterTest : public ::testing::TestWithParam<pair<string, int32_t>> {
public:
+ WriterTest() : mWriter(nullptr), mFileMeta(nullptr), mCurrentTrack(nullptr) {}
+
+ ~WriterTest() {
+ if (mWriter) {
+ mWriter.clear();
+ mWriter = nullptr;
+ }
+ if (mFileMeta) {
+ mFileMeta.clear();
+ mFileMeta = nullptr;
+ }
+ if (mCurrentTrack) {
+ mCurrentTrack.clear();
+ mCurrentTrack = nullptr;
+ }
+ }
+
virtual void SetUp() override {
mNumCsds = 0;
mInputFrameId = 0;
mWriterName = unknown_comp;
mDisableTest = false;
- std::map<std::string, standardWriters> mapWriter = {
+ static const std::map<std::string, standardWriters> mapWriter = {
{"ogg", OGG}, {"aac", AAC}, {"aac_adts", AAC_ADTS}, {"webm", WEBM},
{"mpeg4", MPEG4}, {"amrnb", AMR_NB}, {"amrwb", AMR_WB}, {"mpeg2Ts", MPEG2TS}};
// Find the component type
string writerFormat = GetParam().first;
if (mapWriter.find(writerFormat) != mapWriter.end()) {
- mWriterName = mapWriter[writerFormat];
+ mWriterName = mapWriter.at(writerFormat);
}
if (mWriterName == standardWriters::unknown_comp) {
cout << "[ WARN ] Test Skipped. No specific writer mentioned\n";
@@ -110,10 +127,8 @@
}
virtual void TearDown() override {
- mWriter.clear();
- mFileMeta.clear();
mBufferInfo.clear();
- if (mInputStream) mInputStream.close();
+ if (mInputStream.is_open()) mInputStream.close();
}
void getInputBufferInfo(string inputFileName, string inputInfo);
@@ -149,7 +164,7 @@
void WriterTest::getInputBufferInfo(string inputFileName, string inputInfo) {
std::ifstream eleInfo;
eleInfo.open(inputInfo.c_str());
- CHECK_EQ(eleInfo.is_open(), true);
+ ASSERT_EQ(eleInfo.is_open(), true);
int32_t bytesCount = 0;
uint32_t flags = 0;
int64_t timestamp = 0;
@@ -162,7 +177,7 @@
}
eleInfo.close();
mInputStream.open(inputFileName.c_str(), std::ifstream::binary);
- CHECK_EQ(mInputStream.is_open(), true);
+ ASSERT_EQ(mInputStream.is_open(), true);
}
int32_t WriterTest::createWriter(int32_t fd) {
@@ -258,14 +273,11 @@
string outputFile = OUTPUT_FILE_NAME;
int32_t fd =
open(outputFile.c_str(), O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
- if (fd < 0) return;
+ ASSERT_GE(fd, 0) << "Failed to open output file to dump writer's data";
// Creating writer within a test scope. Destructor should be called when the test ends
- int32_t status = createWriter(fd);
- if (status) {
- cout << "Failed to create writer for output format:" << GetParam().first << "\n";
- ASSERT_TRUE(false);
- }
+ ASSERT_EQ((status_t)OK, createWriter(fd))
+ << "Failed to create writer for output format:" << GetParam().first;
}
TEST_P(WriterTest, WriterTest) {
@@ -276,38 +288,32 @@
string outputFile = OUTPUT_FILE_NAME;
int32_t fd =
open(outputFile.c_str(), O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
- if (fd < 0) return;
+ ASSERT_GE(fd, 0) << "Failed to open output file to dump writer's data";
+
int32_t status = createWriter(fd);
- if (status) {
- cout << "Failed to create writer for output format:" << writerFormat << "\n";
- ASSERT_TRUE(false);
- }
+ ASSERT_EQ((status_t)OK, status) << "Failed to create writer for output format:" << writerFormat;
+
string inputFile = gEnv->getRes();
string inputInfo = gEnv->getRes();
configFormat param;
bool isAudio;
int32_t inputFileIdx = GetParam().second;
getFileDetails(inputFile, inputInfo, param, isAudio, inputFileIdx);
- if (!inputFile.compare(gEnv->getRes())) {
- ALOGV("No input file specified");
- return;
- }
+ ASSERT_NE(inputFile.compare(gEnv->getRes()), 0) << "No input file specified";
+
getInputBufferInfo(inputFile, inputInfo);
status = addWriterSource(isAudio, param);
- if (status) {
- cout << "Failed to add source for " << writerFormat << "Writer \n";
- ASSERT_TRUE(false);
- }
- CHECK_EQ((status_t)OK, mWriter->start(mFileMeta.get()));
+ ASSERT_EQ((status_t)OK, status) << "Failed to add source for " << writerFormat << "Writer";
+
+ status = mWriter->start(mFileMeta.get());
+ ASSERT_EQ((status_t)OK, status);
status = sendBuffersToWriter(mInputStream, mBufferInfo, mInputFrameId, mCurrentTrack, 0,
mBufferInfo.size());
+ ASSERT_EQ((status_t)OK, status) << writerFormat << " writer failed";
mCurrentTrack->stop();
- if (status) {
- cout << writerFormat << " writer failed \n";
- mWriter->stop();
- ASSERT_TRUE(false);
- }
- CHECK_EQ((status_t)OK, mWriter->stop());
+
+ status = mWriter->stop();
+ ASSERT_EQ((status_t)OK, status) << "Failed to stop the writer";
close(fd);
}
@@ -319,59 +325,125 @@
string outputFile = OUTPUT_FILE_NAME;
int32_t fd =
open(outputFile.c_str(), O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
- if (fd < 0) return;
+ ASSERT_GE(fd, 0) << "Failed to open output file to dump writer's data";
+
int32_t status = createWriter(fd);
- if (status) {
- cout << "Failed to create writer for output format:" << writerFormat << "\n";
- ASSERT_TRUE(false);
- }
+ ASSERT_EQ((status_t)OK, status) << "Failed to create writer for output format:" << writerFormat;
+
string inputFile = gEnv->getRes();
string inputInfo = gEnv->getRes();
configFormat param;
bool isAudio;
int32_t inputFileIdx = GetParam().second;
getFileDetails(inputFile, inputInfo, param, isAudio, inputFileIdx);
- if (!inputFile.compare(gEnv->getRes())) {
- ALOGV("No input file specified");
- return;
- }
+ ASSERT_NE(inputFile.compare(gEnv->getRes()), 0) << "No input file specified";
+
getInputBufferInfo(inputFile, inputInfo);
status = addWriterSource(isAudio, param);
- if (status) {
- cout << "Failed to add source for " << writerFormat << "Writer \n";
- ASSERT_TRUE(false);
- }
- CHECK_EQ((status_t)OK, mWriter->start(mFileMeta.get()));
+ ASSERT_EQ((status_t)OK, status) << "Failed to add source for " << writerFormat << "Writer";
+
+ status = mWriter->start(mFileMeta.get());
+ ASSERT_EQ((status_t)OK, status);
status = sendBuffersToWriter(mInputStream, mBufferInfo, mInputFrameId, mCurrentTrack, 0,
mBufferInfo.size() / 4);
- if (status) {
- cout << writerFormat << " writer failed \n";
- mCurrentTrack->stop();
- mWriter->stop();
- ASSERT_TRUE(false);
- }
+ ASSERT_EQ((status_t)OK, status) << writerFormat << " writer failed";
bool isPaused = false;
if ((mWriterName != standardWriters::MPEG2TS) && (mWriterName != standardWriters::MPEG4)) {
- CHECK_EQ((status_t)OK, mWriter->pause());
+ status = mWriter->pause();
+ ASSERT_EQ((status_t)OK, status);
isPaused = true;
}
// In the pause state, writers shouldn't write anything. Testing the writers for the same
int32_t numFramesPaused = mBufferInfo.size() / 4;
- status |= sendBuffersToWriter(mInputStream, mBufferInfo, mInputFrameId, mCurrentTrack,
+ status = sendBuffersToWriter(mInputStream, mBufferInfo, mInputFrameId, mCurrentTrack,
mInputFrameId, numFramesPaused, isPaused);
+ ASSERT_EQ((status_t)OK, status) << writerFormat << " writer failed";
+
if (isPaused) {
- CHECK_EQ((status_t)OK, mWriter->start(mFileMeta.get()));
+ status = mWriter->start(mFileMeta.get());
+ ASSERT_EQ((status_t)OK, status);
}
- status |= sendBuffersToWriter(mInputStream, mBufferInfo, mInputFrameId, mCurrentTrack,
+ status = sendBuffersToWriter(mInputStream, mBufferInfo, mInputFrameId, mCurrentTrack,
mInputFrameId, mBufferInfo.size());
+ ASSERT_EQ((status_t)OK, status) << writerFormat << " writer failed";
mCurrentTrack->stop();
- if (status) {
- cout << writerFormat << " writer failed \n";
- mWriter->stop();
- ASSERT_TRUE(false);
+
+ status = mWriter->stop();
+ ASSERT_EQ((status_t)OK, status) << "Failed to stop the writer";
+ close(fd);
+}
+
+TEST_P(WriterTest, MultiStartStopPauseTest) {
+ // TODO: (b/144821804)
+ // Enable the test for MPE2TS writer
+ if (mDisableTest || mWriterName == standardWriters::MPEG2TS) return;
+ ALOGV("Test writers for multiple start, stop and pause calls");
+
+ string outputFile = OUTPUT_FILE_NAME;
+ int32_t fd =
+ open(outputFile.c_str(), O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
+ ASSERT_GE(fd, 0) << "Failed to open output file to dump writer's data";
+
+ string writerFormat = GetParam().first;
+ int32_t status = createWriter(fd);
+ ASSERT_EQ(status, (status_t)OK) << "Failed to create writer for output format:" << writerFormat;
+
+ string inputFile = gEnv->getRes();
+ string inputInfo = gEnv->getRes();
+ configFormat param;
+ bool isAudio;
+ int32_t inputFileIdx = GetParam().second;
+ getFileDetails(inputFile, inputInfo, param, isAudio, inputFileIdx);
+ ASSERT_NE(inputFile.compare(gEnv->getRes()), 0) << "No input file specified";
+
+ getInputBufferInfo(inputFile, inputInfo);
+ status = addWriterSource(isAudio, param);
+ ASSERT_EQ((status_t)OK, status) << "Failed to add source for " << writerFormat << "Writer";
+
+ // first start should succeed.
+ status = mWriter->start(mFileMeta.get());
+ ASSERT_EQ((status_t)OK, status) << "Could not start the writer";
+
+ // Multiple start() may/may not succeed.
+ // Writers are expected to not crash on multiple start() calls.
+ for (int32_t count = 0; count < kMaxCount; count++) {
+ mWriter->start(mFileMeta.get());
}
- CHECK_EQ((status_t)OK, mWriter->stop());
+
+ status = sendBuffersToWriter(mInputStream, mBufferInfo, mInputFrameId, mCurrentTrack, 0,
+ mBufferInfo.size() / 4);
+ ASSERT_EQ((status_t)OK, status) << writerFormat << " writer failed";
+
+ for (int32_t count = 0; count < kMaxCount; count++) {
+ mWriter->pause();
+ mWriter->start(mFileMeta.get());
+ }
+
+ mWriter->pause();
+ int32_t numFramesPaused = mBufferInfo.size() / 4;
+ status = sendBuffersToWriter(mInputStream, mBufferInfo, mInputFrameId, mCurrentTrack,
+ mInputFrameId, numFramesPaused, true);
+ ASSERT_EQ((status_t)OK, status) << writerFormat << " writer failed";
+
+ for (int32_t count = 0; count < kMaxCount; count++) {
+ mWriter->start(mFileMeta.get());
+ }
+
+ status = sendBuffersToWriter(mInputStream, mBufferInfo, mInputFrameId, mCurrentTrack,
+ mInputFrameId, mBufferInfo.size());
+ ASSERT_EQ((status_t)OK, status) << writerFormat << " writer failed";
+
+ mCurrentTrack->stop();
+
+ // first stop should succeed.
+ status = mWriter->stop();
+ ASSERT_EQ((status_t)OK, status) << "Failed to stop the writer";
+ // Multiple stop() may/may not succeed.
+ // Writers are expected to not crash on multiple stop() calls.
+ for (int32_t count = 0; count < kMaxCount; count++) {
+ mWriter->stop();
+ }
close(fd);
}
diff --git a/media/libstagefright/tests/writer/WriterTestEnvironment.h b/media/libstagefright/tests/writer/WriterTestEnvironment.h
index 34c2baa..99e686f 100644
--- a/media/libstagefright/tests/writer/WriterTestEnvironment.h
+++ b/media/libstagefright/tests/writer/WriterTestEnvironment.h
@@ -25,7 +25,7 @@
class WriterTestEnvironment : public ::testing::Environment {
public:
- WriterTestEnvironment() : res("/sdcard/media/") {}
+ WriterTestEnvironment() : res("/data/local/tmp/") {}
// Parses the command line arguments
int initFromOptions(int argc, char **argv);
diff --git a/media/libstagefright/tests/writer/WriterUtility.cpp b/media/libstagefright/tests/writer/WriterUtility.cpp
index 2ba90a0..f24ccb6 100644
--- a/media/libstagefright/tests/writer/WriterUtility.cpp
+++ b/media/libstagefright/tests/writer/WriterUtility.cpp
@@ -26,7 +26,7 @@
int32_t &inputFrameId, sp<MediaAdapter> ¤tTrack, int32_t offset,
int32_t range, bool isPaused) {
while (1) {
- if (inputFrameId == (int)bufferInfo.size() || inputFrameId >= (offset + range)) break;
+ if (inputFrameId >= (int)bufferInfo.size() || inputFrameId >= (offset + range)) break;
int32_t size = bufferInfo[inputFrameId].size;
char *data = (char *)malloc(size);
if (!data) {
diff --git a/media/libstagefright/tests/writer/WriterUtility.h b/media/libstagefright/tests/writer/WriterUtility.h
index d402798..cdd6246 100644
--- a/media/libstagefright/tests/writer/WriterUtility.h
+++ b/media/libstagefright/tests/writer/WriterUtility.h
@@ -33,6 +33,7 @@
#define CODEC_CONFIG_FLAG 32
constexpr uint32_t kMaxCSDStrlen = 16;
+constexpr uint32_t kMaxCount = 20;
struct BufferInfo {
int32_t size;
diff --git a/media/libstagefright/webm/WebmFrameThread.h b/media/libstagefright/webm/WebmFrameThread.h
index 2dde20a..5aa6feb 100644
--- a/media/libstagefright/webm/WebmFrameThread.h
+++ b/media/libstagefright/webm/WebmFrameThread.h
@@ -21,7 +21,7 @@
#include "LinkedBlockingQueue.h"
#include <datasource/FileSource.h>
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <utils/List.h>
#include <utils/Errors.h>
diff --git a/media/libstagefright/webm/WebmWriter.h b/media/libstagefright/webm/WebmWriter.h
index ffe4c79..ed5bc4c 100644
--- a/media/libstagefright/webm/WebmWriter.h
+++ b/media/libstagefright/webm/WebmWriter.h
@@ -21,7 +21,7 @@
#include "WebmFrameThread.h"
#include "LinkedBlockingQueue.h"
-#include <media/MediaSource.h>
+#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MediaWriter.h>
#include <utils/Errors.h>
diff --git a/media/libstagefright/xmlparser/Android.bp b/media/libstagefright/xmlparser/Android.bp
index 38de831..7ed0e88 100644
--- a/media/libstagefright/xmlparser/Android.bp
+++ b/media/libstagefright/xmlparser/Android.bp
@@ -10,6 +10,7 @@
vndk: {
enabled: true,
},
+ double_loadable: true,
srcs: [
"MediaCodecsXmlParser.cpp",
diff --git a/media/mtp/IMtpHandle.h b/media/mtp/IMtpHandle.h
index fd14b18..0435e82 100644
--- a/media/mtp/IMtpHandle.h
+++ b/media/mtp/IMtpHandle.h
@@ -16,7 +16,7 @@
#ifndef _IMTP_HANDLE_H
#define _IMTP_HANDLE_H
-#include <linux/usb/f_mtp.h>
+#include "f_mtp.h"
namespace android {
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index af69a10..fdf51b1 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -84,8 +84,8 @@
// MTP_OPERATION_SET_OBJECT_PROP_LIST,
// MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC,
// MTP_OPERATION_SEND_OBJECT_PROP_LIST,
- MTP_OPERATION_GET_OBJECT_REFERENCES,
- MTP_OPERATION_SET_OBJECT_REFERENCES,
+// MTP_OPERATION_GET_OBJECT_REFERENCES,
+// MTP_OPERATION_SET_OBJECT_REFERENCES,
// MTP_OPERATION_SKIP,
// Android extension for direct file IO
MTP_OPERATION_GET_PARTIAL_OBJECT_64,
diff --git a/media/mtp/f_mtp.h b/media/mtp/f_mtp.h
new file mode 100644
index 0000000..22ec771
--- /dev/null
+++ b/media/mtp/f_mtp.h
@@ -0,0 +1,43 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ *** This header was automatically generated from a Linux kernel header
+ *** of the same name, to make information necessary for userspace to
+ *** call into the kernel available to libc. It contains only constants,
+ *** structures, and macros generated from the original header, and thus,
+ *** contains no copyrightable information.
+ ***
+ *** To edit the content of this header, modify the corresponding
+ *** source file (e.g. under external/kernel-headers/original/) then
+ *** run bionic/libc/kernel/tools/update_all.py
+ ***
+ *** Any manual change here will be lost the next time this script will
+ *** be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _UAPI_LINUX_USB_F_MTP_H
+#define _UAPI_LINUX_USB_F_MTP_H
+#include <linux/ioctl.h>
+#include <linux/types.h>
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct mtp_file_range {
+ int fd;
+ loff_t offset;
+ int64_t length;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint16_t command;
+ uint32_t transaction_id;
+};
+struct mtp_event {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ size_t length;
+ void * data;
+};
+#define MTP_SEND_FILE _IOW('M', 0, struct mtp_file_range)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define MTP_RECEIVE_FILE _IOW('M', 1, struct mtp_file_range)
+#define MTP_SEND_EVENT _IOW('M', 3, struct mtp_event)
+#define MTP_SEND_FILE_WITH_HEADER _IOW('M', 4, struct mtp_file_range)
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp
index f83d530..a04a962 100644
--- a/media/ndk/Android.bp
+++ b/media/ndk/Android.bp
@@ -72,6 +72,7 @@
header_libs: [
"libmediadrm_headers",
+ "libmediametrics_headers",
],
shared_libs: [
@@ -113,10 +114,6 @@
symbol_file: "libmediandk.map.txt",
versions: ["29"],
},
-
- // Bug: http://b/124522995 libmediandk has linker errors when built with
- // coverage
- native_coverage: false,
}
llndk_library {
@@ -179,6 +176,10 @@
"libcutils",
"android.hardware.graphics.bufferqueue@1.0",
],
+ header_libs: [
+ "libstagefright_foundation_headers",
+ ],
+
cflags: [
"-D__ANDROID_VNDK__",
],
diff --git a/media/tests/benchmark/MediaBenchmarkTest/Android.bp b/media/tests/benchmark/MediaBenchmarkTest/Android.bp
index 91b03f1..d80d9a5 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/Android.bp
+++ b/media/tests/benchmark/MediaBenchmarkTest/Android.bp
@@ -17,11 +17,13 @@
android_test {
name: "MediaBenchmarkTest",
+ defaults: [
+ "MediaBenchmark-defaults",
+ ],
+
// Include all the test code
srcs: ["src/androidTest/**/*.java"],
- sdk_version: "system_current",
-
resource_dirs: ["res"],
libs: [
@@ -29,6 +31,10 @@
"android.test.base",
],
+ jni_libs: [
+ "libmediabenchmark_jni",
+ ],
+
static_libs: [
"libMediaBenchmark",
"junit",
@@ -39,12 +45,22 @@
android_library {
name: "libMediaBenchmark",
+ defaults: [
+ "MediaBenchmark-defaults",
+ ],
+
// Include all the libraries
srcs: ["src/main/**/*.java"],
- sdk_version: "system_current",
-
static_libs: [
"androidx.test.core",
],
}
+
+java_defaults {
+ name: "MediaBenchmark-defaults",
+
+ sdk_version: "system_current",
+ min_sdk_version: "28",
+ target_sdk_version: "29",
+}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/AndroidTest.xml b/media/tests/benchmark/MediaBenchmarkTest/AndroidTest.xml
index 89d6ce2..1179d6c 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/AndroidTest.xml
+++ b/media/tests/benchmark/MediaBenchmarkTest/AndroidTest.xml
@@ -15,6 +15,7 @@
-->
<configuration description="Runs Media Benchmark Tests">
<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="cleanup-apks" value="false" />
<option name="test-file-name" value="MediaBenchmarkTest.apk" />
</target_preparer>
diff --git a/media/tests/benchmark/MediaBenchmarkTest/build.gradle b/media/tests/benchmark/MediaBenchmarkTest/build.gradle
index b0ee692..b2aee1a 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/build.gradle
+++ b/media/tests/benchmark/MediaBenchmarkTest/build.gradle
@@ -30,7 +30,7 @@
compileSdkVersion 29
defaultConfig {
applicationId "com.android.media.benchmark"
- minSdkVersion 21
+ minSdkVersion 28
targetSdkVersion 29
versionCode 1
versionName "1.0"
@@ -48,6 +48,18 @@
manifest.srcFile 'AndroidManifest.xml'
}
}
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+ externalNativeBuild {
+ cmake {
+ path "src/main/cpp/CMakeLists.txt"
+ version "3.10.2"
+ }
+ }
}
repositories {
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/DecoderTest.java b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/DecoderTest.java
index be2633d..c41f198 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/DecoderTest.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/DecoderTest.java
@@ -27,7 +27,10 @@
import com.android.media.benchmark.library.CodecUtils;
import com.android.media.benchmark.library.Decoder;
import com.android.media.benchmark.library.Extractor;
+import com.android.media.benchmark.library.Native;
+import com.android.media.benchmark.library.Stats;
+import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -44,12 +47,17 @@
import java.util.Arrays;
import java.util.Collection;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+
@RunWith(Parameterized.class)
public class DecoderTest {
private static final Context mContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
private static final String mInputFilePath = mContext.getString(R.string.input_file_path);
private static final String mOutputFilePath = mContext.getString(R.string.output_file_path);
+ private static final String mStatsFile =
+ mContext.getExternalFilesDir(null) + "/Decoder." + System.currentTimeMillis() + ".csv";
private static final String TAG = "DecoderTest";
private static final long PER_TEST_TIMEOUT_MS = 60000;
private static final boolean DEBUG = false;
@@ -101,97 +109,112 @@
{"crowd_1920x1080_25fps_4000kbps_h265.mkv", true}});
}
+ @BeforeClass
+ public static void writeStatsHeaderToFile() throws IOException {
+ Stats mStats = new Stats();
+ boolean status = mStats.writeStatsHeader(mStatsFile);
+ assertTrue("Unable to open stats file for writing!", status);
+ Log.d(TAG, "Saving Benchmark results in: " + mStatsFile);
+ }
+
@Test(timeout = PER_TEST_TIMEOUT_MS)
public void testDecoder() throws IOException {
File inputFile = new File(mInputFilePath + mInputFile);
- if (inputFile.exists()) {
- FileInputStream fileInput = new FileInputStream(inputFile);
- FileDescriptor fileDescriptor = fileInput.getFD();
- Extractor extractor = new Extractor();
- int trackCount = extractor.setUpExtractor(fileDescriptor);
- ArrayList<ByteBuffer> inputBuffer = new ArrayList<>();
- ArrayList<MediaCodec.BufferInfo> frameInfo = new ArrayList<>();
- if (trackCount <= 0) {
- Log.e(TAG, "Extraction failed. No tracks for file: " + mInputFile);
- return;
- }
- for (int currentTrack = 0; currentTrack < trackCount; currentTrack++) {
- extractor.selectExtractorTrack(currentTrack);
- MediaFormat format = extractor.getFormat(currentTrack);
- String mime = format.getString(MediaFormat.KEY_MIME);
- ArrayList<String> mediaCodecs = CodecUtils.selectCodecs(mime, false);
- if (mediaCodecs.size() <= 0) {
- Log.e(TAG,
- "No suitable codecs found for file: " + mInputFile
- + " track : " + currentTrack + " mime: " + mime);
- continue;
+ assertTrue("Cannot find " + mInputFile + " in directory " + mInputFilePath,
+ inputFile.exists());
+ FileInputStream fileInput = new FileInputStream(inputFile);
+ FileDescriptor fileDescriptor = fileInput.getFD();
+ Extractor extractor = new Extractor();
+ int trackCount = extractor.setUpExtractor(fileDescriptor);
+ assertTrue("Extraction failed. No tracks for file: " + mInputFile, (trackCount > 0));
+ ArrayList<ByteBuffer> inputBuffer = new ArrayList<>();
+ ArrayList<MediaCodec.BufferInfo> frameInfo = new ArrayList<>();
+ for (int currentTrack = 0; currentTrack < trackCount; currentTrack++) {
+ extractor.selectExtractorTrack(currentTrack);
+ MediaFormat format = extractor.getFormat(currentTrack);
+ String mime = format.getString(MediaFormat.KEY_MIME);
+ ArrayList<String> mediaCodecs = CodecUtils.selectCodecs(mime, false);
+ assertTrue("No suitable codecs found for file: " + mInputFile + " track : " +
+ currentTrack + " mime: " + mime, (mediaCodecs.size() > 0));
+
+ // Get samples from extractor
+ int sampleSize;
+ do {
+ sampleSize = extractor.getFrameSample();
+ MediaCodec.BufferInfo bufInfo = new MediaCodec.BufferInfo();
+ MediaCodec.BufferInfo info = extractor.getBufferInfo();
+ ByteBuffer dataBuffer = ByteBuffer.allocate(info.size);
+ dataBuffer.put(extractor.getFrameBuffer().array(), 0, info.size);
+ bufInfo.set(info.offset, info.size, info.presentationTimeUs, info.flags);
+ inputBuffer.add(dataBuffer);
+ frameInfo.add(bufInfo);
+ if (DEBUG) {
+ Log.d(TAG, "Extracted bufInfo: flag = " + bufInfo.flags + " timestamp = " +
+ bufInfo.presentationTimeUs + " size = " + bufInfo.size);
}
- // Get samples from extractor
- int sampleSize;
- do {
- sampleSize = extractor.getFrameSample();
- MediaCodec.BufferInfo bufInfo = new MediaCodec.BufferInfo();
- MediaCodec.BufferInfo info = extractor.getBufferInfo();
- ByteBuffer dataBuffer = ByteBuffer.allocate(info.size);
- dataBuffer.put(extractor.getFrameBuffer().array(), 0, info.size);
- bufInfo.set(info.offset, info.size, info.presentationTimeUs, info.flags);
- inputBuffer.add(dataBuffer);
- frameInfo.add(bufInfo);
- if (DEBUG) {
- Log.d(TAG,
- "Extracted bufInfo: flag = " + bufInfo.flags + " timestamp = "
- + bufInfo.presentationTimeUs + " size = " + bufInfo.size);
+ } while (sampleSize > 0);
+ for (String codecName : mediaCodecs) {
+ FileOutputStream decodeOutputStream = null;
+ if (WRITE_OUTPUT) {
+ if (!Paths.get(mOutputFilePath).toFile().exists()) {
+ Files.createDirectories(Paths.get(mOutputFilePath));
}
- } while (sampleSize > 0);
- for (String codecName : mediaCodecs) {
- FileOutputStream decodeOutputStream = null;
- if (WRITE_OUTPUT) {
- if (!Paths.get(mOutputFilePath).toFile().exists()) {
- Files.createDirectories(Paths.get(mOutputFilePath));
- }
- File outFile = new File(mOutputFilePath + "decoder.out");
- if (outFile.exists()) {
- if (!outFile.delete()) {
- Log.e(TAG, " Unable to delete existing file" + outFile.toString());
- }
- }
- if (outFile.createNewFile()) {
- decodeOutputStream = new FileOutputStream(outFile);
- } else {
- Log.e(TAG, "Unable to create file: " + outFile.toString());
- }
+ File outFile = new File(mOutputFilePath + "decoder.out");
+ if (outFile.exists()) {
+ assertTrue(" Unable to delete existing file" + outFile.toString(),
+ outFile.delete());
}
- Decoder decoder = new Decoder();
- decoder.setupDecoder(decodeOutputStream);
- int status =
- decoder.decode(inputBuffer, frameInfo, mAsyncMode, format, codecName);
- decoder.deInitCodec();
- if (status == 0) {
- decoder.dumpStatistics(
- mInputFile + " " + codecName, extractor.getClipDuration());
- Log.i(TAG,
- "Decoding Successful for file: " + mInputFile
- + " with codec: " + codecName);
- } else {
- Log.e(TAG,
- "Decoder returned error " + status + " for file: " + mInputFile
- + " with codec: " + codecName);
- }
- decoder.resetDecoder();
- if (decodeOutputStream != null) {
- decodeOutputStream.close();
- }
+ assertTrue("Unable to create file: " + outFile.toString(),
+ outFile.createNewFile());
+ decodeOutputStream = new FileOutputStream(outFile);
}
- extractor.unselectExtractorTrack(currentTrack);
- inputBuffer.clear();
- frameInfo.clear();
+ Decoder decoder = new Decoder();
+ decoder.setupDecoder(decodeOutputStream);
+ int status = decoder.decode(inputBuffer, frameInfo, mAsyncMode, format, codecName);
+ decoder.deInitCodec();
+ assertEquals("Decoder returned error " + status + " for file: " + mInputFile +
+ " with codec: " + codecName, 0, status);
+ decoder.dumpStatistics(mInputFile, codecName, (mAsyncMode ? "async" : "sync"),
+ extractor.getClipDuration(), mStatsFile);
+ Log.i(TAG, "Decoding Successful for file: " + mInputFile + " with codec: " +
+ codecName);
+ decoder.resetDecoder();
+ if (decodeOutputStream != null) {
+ decodeOutputStream.close();
+ }
}
- extractor.deinitExtractor();
- fileInput.close();
- } else {
- Log.w(TAG,
- "Warning: Test Skipped. Cannot find " + mInputFile + " in directory "
- + mInputFilePath);
+ extractor.unselectExtractorTrack(currentTrack);
+ inputBuffer.clear();
+ frameInfo.clear();
}
+ extractor.deinitExtractor();
+ fileInput.close();
+ }
+
+ @Test
+ public void testNativeDecoder() throws IOException {
+ File inputFile = new File(mInputFilePath + mInputFile);
+ assertTrue("Cannot find " + mInputFile + " in directory " + mInputFilePath,
+ inputFile.exists());
+ FileInputStream fileInput = new FileInputStream(inputFile);
+ FileDescriptor fileDescriptor = fileInput.getFD();
+ Extractor extractor = new Extractor();
+ int trackCount = extractor.setUpExtractor(fileDescriptor);
+ assertTrue("Extraction failed. No tracks for file: ", trackCount > 0);
+ for (int currentTrack = 0; currentTrack < trackCount; currentTrack++) {
+ extractor.selectExtractorTrack(currentTrack);
+ MediaFormat format = extractor.getFormat(currentTrack);
+ String mime = format.getString(MediaFormat.KEY_MIME);
+ ArrayList<String> mediaCodecs = CodecUtils.selectCodecs(mime, false);
+ for (String codecName : mediaCodecs) {
+ Log.i("Test: %s\n", mInputFile);
+ Native nativeDecoder = new Native();
+ int status = nativeDecoder.Decode(
+ mInputFilePath, mInputFile, mStatsFile, codecName, mAsyncMode);
+ assertEquals("Decoder returned error " + status + " for file: " + mInputFile, 0,
+ status);
+ }
+ }
+ fileInput.close();
}
}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/EncoderTest.java b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/EncoderTest.java
index 9db9c84..831467a 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/EncoderTest.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/EncoderTest.java
@@ -28,7 +28,10 @@
import com.android.media.benchmark.library.Decoder;
import com.android.media.benchmark.library.Encoder;
import com.android.media.benchmark.library.Extractor;
+import com.android.media.benchmark.library.Native;
+import com.android.media.benchmark.library.Stats;
+import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -37,18 +40,24 @@
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+
@RunWith(Parameterized.class)
public class EncoderTest {
private static final Context mContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
private static final String mInputFilePath = mContext.getString(R.string.input_file_path);
private static final String mOutputFilePath = mContext.getString(R.string.output_file_path);
+ private static final String mStatsFile =
+ mContext.getExternalFilesDir(null) + "/Encoder." + System.currentTimeMillis() + ".csv";
private static final String TAG = "EncoderTest";
private static final long PER_TEST_TIMEOUT_MS = 120000;
private static final boolean DEBUG = false;
@@ -56,7 +65,6 @@
private static final int ENCODE_DEFAULT_FRAME_RATE = 25;
private static final int ENCODE_DEFAULT_BIT_RATE = 8000000 /* 8 Mbps */;
private static final int ENCODE_MIN_BIT_RATE = 600000 /* 600 Kbps */;
-
private String mInputFile;
@Parameterized.Parameters
@@ -81,192 +89,205 @@
this.mInputFile = inputFileName;
}
+ @BeforeClass
+ public static void writeStatsHeaderToFile() throws IOException {
+ Stats mStats = new Stats();
+ boolean status = mStats.writeStatsHeader(mStatsFile);
+ assertTrue("Unable to open stats file for writing!", status);
+ Log.d(TAG, "Saving Benchmark results in: " + mStatsFile);
+ }
+
@Test(timeout = PER_TEST_TIMEOUT_MS)
public void sampleEncoderTest() throws Exception {
int status;
int frameSize;
-
//Parameters for video
int width = 0;
int height = 0;
int profile = 0;
int level = 0;
int frameRate = 0;
-
//Parameters for audio
int bitRate = 0;
int sampleRate = 0;
int numChannels = 0;
-
File inputFile = new File(mInputFilePath + mInputFile);
- if (inputFile.exists()) {
- FileInputStream fileInput = new FileInputStream(inputFile);
- FileDescriptor fileDescriptor = fileInput.getFD();
- Extractor extractor = new Extractor();
- int trackCount = extractor.setUpExtractor(fileDescriptor);
- if (trackCount <= 0) {
- Log.e(TAG, "Extraction failed. No tracks for file: " + mInputFile);
- return;
+ assertTrue("Cannot find " + mInputFile + " in directory " + mInputFilePath,
+ inputFile.exists());
+ FileInputStream fileInput = new FileInputStream(inputFile);
+ FileDescriptor fileDescriptor = fileInput.getFD();
+ Extractor extractor = new Extractor();
+ int trackCount = extractor.setUpExtractor(fileDescriptor);
+ assertTrue("Extraction failed. No tracks for file: " + mInputFile, (trackCount > 0));
+ ArrayList<ByteBuffer> inputBuffer = new ArrayList<>();
+ ArrayList<MediaCodec.BufferInfo> frameInfo = new ArrayList<>();
+ for (int currentTrack = 0; currentTrack < trackCount; currentTrack++) {
+ extractor.selectExtractorTrack(currentTrack);
+ MediaFormat format = extractor.getFormat(currentTrack);
+ // Get samples from extractor
+ int sampleSize;
+ do {
+ sampleSize = extractor.getFrameSample();
+ MediaCodec.BufferInfo bufInfo = new MediaCodec.BufferInfo();
+ MediaCodec.BufferInfo info = extractor.getBufferInfo();
+ ByteBuffer dataBuffer = ByteBuffer.allocate(info.size);
+ dataBuffer.put(extractor.getFrameBuffer().array(), 0, info.size);
+ bufInfo.set(info.offset, info.size, info.presentationTimeUs, info.flags);
+ inputBuffer.add(dataBuffer);
+ frameInfo.add(bufInfo);
+ if (DEBUG) {
+ Log.d(TAG, "Extracted bufInfo: flag = " + bufInfo.flags + " timestamp = " +
+ bufInfo.presentationTimeUs + " size = " + bufInfo.size);
+ }
+ } while (sampleSize > 0);
+ int tid = android.os.Process.myTid();
+ File decodedFile = new File(mContext.getFilesDir() + "/decoder_" + tid + ".out");
+ FileOutputStream decodeOutputStream = new FileOutputStream(decodedFile);
+ Decoder decoder = new Decoder();
+ decoder.setupDecoder(decodeOutputStream);
+ status = decoder.decode(inputBuffer, frameInfo, false, format, "");
+ assertEquals("Decoder returned error " + status + " for file: " + mInputFile, 0,
+ status);
+ decoder.deInitCodec();
+ extractor.unselectExtractorTrack(currentTrack);
+ inputBuffer.clear();
+ frameInfo.clear();
+ if (decodeOutputStream != null) {
+ decodeOutputStream.close();
}
- ArrayList<ByteBuffer> inputBuffer = new ArrayList<>();
- ArrayList<MediaCodec.BufferInfo> frameInfo = new ArrayList<>();
- for (int currentTrack = 0; currentTrack < trackCount; currentTrack++) {
- extractor.selectExtractorTrack(currentTrack);
- MediaFormat format = extractor.getFormat(currentTrack);
- // Get samples from extractor
- int sampleSize;
- do {
- sampleSize = extractor.getFrameSample();
- MediaCodec.BufferInfo bufInfo = new MediaCodec.BufferInfo();
- MediaCodec.BufferInfo info = extractor.getBufferInfo();
- ByteBuffer dataBuffer = ByteBuffer.allocate(info.size);
- dataBuffer.put(extractor.getFrameBuffer().array(), 0, info.size);
- bufInfo.set(info.offset, info.size, info.presentationTimeUs, info.flags);
- inputBuffer.add(dataBuffer);
- frameInfo.add(bufInfo);
+ String mime = format.getString(MediaFormat.KEY_MIME);
+ ArrayList<String> mediaCodecs = CodecUtils.selectCodecs(mime, true);
+ assertTrue("No suitable codecs found for file: " + mInputFile + " track : " +
+ currentTrack + " mime: " + mime, (mediaCodecs.size() > 0));
+ Boolean[] encodeMode = {true, false};
+ /* Encoding the decoder's output */
+ for (Boolean asyncMode : encodeMode) {
+ for (String codecName : mediaCodecs) {
+ FileOutputStream encodeOutputStream = null;
+ if (WRITE_OUTPUT) {
+ File outEncodeFile = new File(mOutputFilePath + "encoder.out");
+ if (outEncodeFile.exists()) {
+ assertTrue(" Unable to delete existing file" + outEncodeFile.toString(),
+ outEncodeFile.delete());
+ }
+ assertTrue("Unable to create file to write encoder output: " +
+ outEncodeFile.toString(), outEncodeFile.createNewFile());
+ encodeOutputStream = new FileOutputStream(outEncodeFile);
+ }
+ File rawFile = new File(mContext.getFilesDir() + "/decoder_" + tid + ".out");
+ assertTrue("Cannot open file to write decoded output", rawFile.exists());
if (DEBUG) {
- Log.d(TAG, "Extracted bufInfo: flag = " + bufInfo.flags + " timestamp = " +
- bufInfo.presentationTimeUs + " size = " + bufInfo.size);
+ Log.i(TAG, "Path of decoded input file: " + rawFile.toString());
}
- } while (sampleSize > 0);
-
- int tid = android.os.Process.myTid();
- File decodedFile = new File(mContext.getFilesDir() + "/decoder_" + tid + ".out");
- FileOutputStream decodeOutputStream = new FileOutputStream(decodedFile);
- Decoder decoder = new Decoder();
- decoder.setupDecoder(decodeOutputStream);
- status = decoder.decode(inputBuffer, frameInfo, false, format, "");
- if (status == 0) {
- Log.i(TAG, "Decoding complete.");
- } else {
- Log.e(TAG, "Decode returned error. Encoding did not take place." + status);
- return;
- }
- decoder.deInitCodec();
- extractor.unselectExtractorTrack(currentTrack);
- inputBuffer.clear();
- frameInfo.clear();
- if (decodeOutputStream != null) {
- decodeOutputStream.close();
- }
- String mime = format.getString(MediaFormat.KEY_MIME);
- ArrayList<String> mediaCodecs = CodecUtils.selectCodecs(mime, true);
- if (mediaCodecs.size() <= 0) {
- Log.e(TAG, "No suitable codecs found for file: " + mInputFile + " track : " +
- currentTrack + " mime: " + mime);
- return;
- }
- Boolean[] encodeMode = {true, false};
- /* Encoding the decoder's output */
- for (Boolean asyncMode : encodeMode) {
- for (String codecName : mediaCodecs) {
- FileOutputStream encodeOutputStream = null;
- if (WRITE_OUTPUT) {
- File outEncodeFile = new File(mOutputFilePath + "encoder.out");
- if (outEncodeFile.exists()) {
- if (!outEncodeFile.delete()) {
- Log.e(TAG, "Unable to delete existing file" +
- decodedFile.toString());
- }
- }
- if (outEncodeFile.createNewFile()) {
- encodeOutputStream = new FileOutputStream(outEncodeFile);
+ FileInputStream eleStream = new FileInputStream(rawFile);
+ if (mime.startsWith("video/")) {
+ width = format.getInteger(MediaFormat.KEY_WIDTH);
+ height = format.getInteger(MediaFormat.KEY_HEIGHT);
+ if (format.containsKey(MediaFormat.KEY_FRAME_RATE)) {
+ frameRate = format.getInteger(MediaFormat.KEY_FRAME_RATE);
+ } else if (frameRate <= 0) {
+ frameRate = ENCODE_DEFAULT_FRAME_RATE;
+ }
+ if (format.containsKey(MediaFormat.KEY_BIT_RATE)) {
+ bitRate = format.getInteger(MediaFormat.KEY_BIT_RATE);
+ } else if (bitRate <= 0) {
+ if (mime.contains("video/3gpp") || mime.contains("video/mp4v-es")) {
+ bitRate = ENCODE_MIN_BIT_RATE;
} else {
- Log.e(TAG, "Unable to create file to write encoder output: " +
- outEncodeFile.toString());
+ bitRate = ENCODE_DEFAULT_BIT_RATE;
}
}
- File rawFile =
- new File(mContext.getFilesDir() + "/decoder_" + tid + ".out");
- if (rawFile.exists()) {
- if (DEBUG) {
- Log.i(TAG, "Path of decoded input file: " + rawFile.toString());
- }
- FileInputStream eleStream = new FileInputStream(rawFile);
- if (mime.startsWith("video/")) {
- width = format.getInteger(MediaFormat.KEY_WIDTH);
- height = format.getInteger(MediaFormat.KEY_HEIGHT);
- if (format.containsKey(MediaFormat.KEY_FRAME_RATE)) {
- frameRate = format.getInteger(MediaFormat.KEY_FRAME_RATE);
- } else if (frameRate <= 0) {
- frameRate = ENCODE_DEFAULT_FRAME_RATE;
- }
- if (format.containsKey(MediaFormat.KEY_BIT_RATE)) {
- bitRate = format.getInteger(MediaFormat.KEY_BIT_RATE);
- } else if (bitRate <= 0) {
- if (mime.contains("video/3gpp") ||
- mime.contains("video/mp4v-es")) {
- bitRate = ENCODE_MIN_BIT_RATE;
- } else {
- bitRate = ENCODE_DEFAULT_BIT_RATE;
- }
- }
- if (format.containsKey(MediaFormat.KEY_PROFILE)) {
- profile = format.getInteger(MediaFormat.KEY_PROFILE);
- }
- if (format.containsKey(MediaFormat.KEY_PROFILE)) {
- level = format.getInteger(MediaFormat.KEY_LEVEL);
- }
- } else {
- sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
- numChannels = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
- bitRate = sampleRate * numChannels * 16;
- }
- /*Setup Encode Format*/
- MediaFormat encodeFormat;
- if (mime.startsWith("video/")) {
- frameSize = width * height * 3 / 2;
- encodeFormat = MediaFormat.createVideoFormat(mime, width, height);
- encodeFormat.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
- encodeFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
- encodeFormat.setInteger(MediaFormat.KEY_PROFILE, profile);
- encodeFormat.setInteger(MediaFormat.KEY_LEVEL, level);
- encodeFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
- encodeFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, frameSize);
- } else {
- encodeFormat = MediaFormat
- .createAudioFormat(mime, sampleRate, numChannels);
- encodeFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
- frameSize = 4096;
- }
- Encoder encoder = new Encoder();
- encoder.setupEncoder(encodeOutputStream, eleStream);
- status = encoder.encode(codecName, encodeFormat, mime, frameRate,
- sampleRate, frameSize, asyncMode);
- encoder.deInitEncoder();
- if (status == 0) {
- encoder.dumpStatistics(mInputFile + "with " + codecName + " for " +
- "aSyncMode = " + asyncMode, extractor.getClipDuration());
- Log.i(TAG, "Encoding complete for file: " + mInputFile +
- " with codec: " + codecName + " for aSyncMode = " +
- asyncMode);
- } else {
- Log.e(TAG,
- codecName + " encoder returned error " + status + " for " +
- "file:" + " " + mInputFile);
- }
- encoder.resetEncoder();
- eleStream.close();
- if (encodeOutputStream != null) {
- encodeOutputStream.close();
- }
+ if (format.containsKey(MediaFormat.KEY_PROFILE)) {
+ profile = format.getInteger(MediaFormat.KEY_PROFILE);
}
- }
- }
- //Cleanup temporary input file
- if (decodedFile.exists()) {
- if (decodedFile.delete()) {
- Log.i(TAG, "Successfully deleted decoded file");
+ if (format.containsKey(MediaFormat.KEY_PROFILE)) {
+ level = format.getInteger(MediaFormat.KEY_LEVEL);
+ }
} else {
- Log.e(TAG, "Unable to delete decoded file");
+ sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
+ numChannels = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
+ bitRate = sampleRate * numChannels * 16;
}
+ /*Setup Encode Format*/
+ MediaFormat encodeFormat;
+ if (mime.startsWith("video/")) {
+ frameSize = width * height * 3 / 2;
+ encodeFormat = MediaFormat.createVideoFormat(mime, width, height);
+ encodeFormat.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
+ encodeFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
+ encodeFormat.setInteger(MediaFormat.KEY_PROFILE, profile);
+ encodeFormat.setInteger(MediaFormat.KEY_LEVEL, level);
+ encodeFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
+ encodeFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, frameSize);
+ } else {
+ encodeFormat = MediaFormat.createAudioFormat(mime, sampleRate, numChannels);
+ encodeFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
+ frameSize = 4096;
+ }
+ Encoder encoder = new Encoder();
+ encoder.setupEncoder(encodeOutputStream, eleStream);
+ status = encoder.encode(codecName, encodeFormat, mime, frameRate, sampleRate,
+ frameSize, asyncMode);
+ encoder.deInitEncoder();
+ assertEquals(
+ codecName + " encoder returned error " + status + " for " + "file:" +
+ " " + mInputFile, 0, status);
+ encoder.dumpStatistics(mInputFile, codecName, (asyncMode ? "async" : "sync"),
+ extractor.getClipDuration(), mStatsFile);
+ Log.i(TAG, "Encoding complete for file: " + mInputFile + " with codec: " +
+ codecName + " for aSyncMode = " + asyncMode);
+ encoder.resetEncoder();
+ eleStream.close();
+ if (encodeOutputStream != null) {
+ encodeOutputStream.close();
+ }
+
}
}
- extractor.deinitExtractor();
- fileInput.close();
- } else {
- Log.w(TAG, "Warning: Test Skipped. Cannot find " + mInputFile + " in directory " +
- mInputFilePath);
+ //Cleanup temporary input file
+ if (decodedFile.exists()) {
+ assertTrue(" Unable to delete decoded file" + decodedFile.toString(),
+ decodedFile.delete());
+ Log.i(TAG, "Successfully deleted decoded file");
+ }
}
+ extractor.deinitExtractor();
+ fileInput.close();
+ }
+
+ @Test
+ public void testNativeEncoder() throws Exception {
+ File inputFile = new File(mInputFilePath + mInputFile);
+ assertTrue("Cannot find " + mInputFile + " in directory " + mInputFilePath,
+ inputFile.exists());
+ int tid = android.os.Process.myTid();
+ final String mDecodedFile = mContext.getFilesDir() + "/decoder_" + tid + ".out";
+ FileInputStream fileInput = new FileInputStream(inputFile);
+ FileDescriptor fileDescriptor = fileInput.getFD();
+ Extractor extractor = new Extractor();
+ int trackCount = extractor.setUpExtractor(fileDescriptor);
+ assertTrue("Extraction failed. No tracks for file: ", trackCount > 0);
+ for (int currentTrack = 0; currentTrack < trackCount; currentTrack++) {
+ extractor.selectExtractorTrack(currentTrack);
+ MediaFormat format = extractor.getFormat(currentTrack);
+ String mime = format.getString(MediaFormat.KEY_MIME);
+ ArrayList<String> mediaCodecs = CodecUtils.selectCodecs(mime, true);
+ // Encoding the decoder's output
+ for (String codecName : mediaCodecs) {
+ Native nativeEncoder = new Native();
+ int status = nativeEncoder.Encode(
+ mInputFilePath, mInputFile, mDecodedFile, mStatsFile, codecName);
+ assertEquals(
+ codecName + " encoder returned error " + status + " for " + "file:" + " " +
+ mInputFile, 0, status);
+ }
+ }
+ File decodedFile = new File(mDecodedFile);
+ // Cleanup temporary input file
+ if (decodedFile.exists()) {
+ assertTrue("Unable to delete - " + mDecodedFile, decodedFile.delete());
+ Log.i(TAG, "Successfully deleted - " + mDecodedFile);
+ }
+ fileInput.close();
}
}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/ExtractorTest.java b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/ExtractorTest.java
index a02011c..6b7aad1 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/ExtractorTest.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/ExtractorTest.java
@@ -18,12 +18,16 @@
import com.android.media.benchmark.R;
import com.android.media.benchmark.library.Extractor;
+import com.android.media.benchmark.library.Native;
+import com.android.media.benchmark.library.Stats;
import android.content.Context;
+import android.media.MediaFormat;
import android.util.Log;
import androidx.test.platform.app.InstrumentationRegistry;
+import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -31,19 +35,23 @@
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+
+import static org.junit.Assert.assertTrue;
@RunWith(Parameterized.class)
public class ExtractorTest {
private static Context mContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
private static final String mInputFilePath = mContext.getString(R.string.input_file_path);
+ private static final String mStatsFile = mContext.getExternalFilesDir(null) + "/Extractor."
+ + System.currentTimeMillis() + ".csv";
private static final String TAG = "ExtractorTest";
private String mInputFileName;
private int mTrackId;
@@ -71,22 +79,43 @@
this.mTrackId = track;
}
+ @BeforeClass
+ public static void writeStatsHeaderToFile() throws IOException {
+ Stats mStats = new Stats();
+ boolean status = mStats.writeStatsHeader(mStatsFile);
+ assertTrue("Unable to open stats file for writing!", status);
+ Log.d(TAG, "Saving Benchmark results in: " + mStatsFile);
+ }
+
@Test
public void sampleExtractTest() throws IOException {
- int status = -1;
File inputFile = new File(mInputFilePath + mInputFileName);
- if (inputFile.exists()) {
- FileInputStream fileInput = new FileInputStream(inputFile);
- FileDescriptor fileDescriptor = fileInput.getFD();
- Extractor extractor = new Extractor();
- extractor.setUpExtractor(fileDescriptor);
- status = extractor.extractSample(mTrackId);
- extractor.deinitExtractor();
- extractor.dumpStatistics(mInputFileName);
- fileInput.close();
- } else {
- Log.e(TAG, "Cannot find " + mInputFileName + " in directory " + mInputFilePath);
- }
- assertThat(status, is(equalTo(0)));
+ assertTrue("Cannot find " + mInputFileName + " in directory " + mInputFilePath,
+ inputFile.exists());
+ FileInputStream fileInput = new FileInputStream(inputFile);
+ FileDescriptor fileDescriptor = fileInput.getFD();
+ Extractor extractor = new Extractor();
+ extractor.setUpExtractor(fileDescriptor);
+ MediaFormat format = extractor.getFormat(mTrackId);
+ String mime = format.getString(MediaFormat.KEY_MIME);
+ int status = extractor.extractSample(mTrackId);
+ assertEquals("Extraction failed for " + mInputFileName, 0, status);
+ Log.i(TAG, "Extracted " + mInputFileName + " successfully.");
+ extractor.deinitExtractor();
+ extractor.dumpStatistics(mInputFileName, mime, mStatsFile);
+ fileInput.close();
+ }
+
+ @Test
+ public void sampleExtractNativeTest() throws IOException {
+ Native nativeExtractor = new Native();
+ File inputFile = new File(mInputFilePath + mInputFileName);
+ assertTrue("Cannot find " + mInputFileName + " in directory " + mInputFilePath,
+ inputFile.exists());
+ FileInputStream fileInput = new FileInputStream(inputFile);
+ int status = nativeExtractor.Extract(mInputFilePath, mInputFileName, mStatsFile);
+ fileInput.close();
+ assertEquals("Extraction failed for " + mInputFileName, 0, status);
+ Log.i(TAG, "Extracted " + mInputFileName + " successfully.");
}
}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/MuxerTest.java b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/MuxerTest.java
index 8c3080c..2efdba2 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/MuxerTest.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/MuxerTest.java
@@ -18,6 +18,8 @@
import com.android.media.benchmark.R;
import com.android.media.benchmark.library.Extractor;
import com.android.media.benchmark.library.Muxer;
+import com.android.media.benchmark.library.Native;
+import com.android.media.benchmark.library.Stats;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -27,6 +29,7 @@
import android.media.MediaMuxer;
import android.util.Log;
+import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -34,6 +37,7 @@
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
@@ -42,15 +46,19 @@
import java.util.Hashtable;
import java.util.Map;
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import static org.junit.Assert.assertTrue;
@RunWith(Parameterized.class)
public class MuxerTest {
private static Context mContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
private static final String mInputFilePath = mContext.getString(R.string.input_file_path);
+ private static final String mStatsFile =
+ mContext.getExternalFilesDir(null) + "/Muxer." + System.currentTimeMillis() + ".csv";
private static final String TAG = "MuxerTest";
private static final Map<String, Integer> mMapFormat = new Hashtable<String, Integer>() {
{
@@ -93,60 +101,80 @@
this.mFormat = outputFormat;
}
+ @BeforeClass
+ public static void writeStatsHeaderToFile() throws IOException {
+ Stats mStats = new Stats();
+ boolean status = mStats.writeStatsHeader(mStatsFile);
+ assertTrue("Unable to open stats file for writing!", status);
+ Log.d(TAG, "Saving Benchmark results in: " + mStatsFile);
+ }
+
@Test
public void sampleMuxerTest() throws IOException {
- int status = -1;
File inputFile = new File(mInputFilePath + mInputFileName);
- if (inputFile.exists()) {
- FileInputStream fileInput = new FileInputStream(inputFile);
- FileDescriptor fileDescriptor = fileInput.getFD();
- ArrayList<ByteBuffer> inputBuffer = new ArrayList<>();
- ArrayList<MediaCodec.BufferInfo> inputBufferInfo = new ArrayList<>();
- Extractor extractor = new Extractor();
- int trackCount = extractor.setUpExtractor(fileDescriptor);
- for (int currentTrack = 0; currentTrack < trackCount; currentTrack++) {
- extractor.selectExtractorTrack(currentTrack);
- while (true) {
- int sampleSize = extractor.getFrameSample();
- MediaCodec.BufferInfo bufferInfo = extractor.getBufferInfo();
- MediaCodec.BufferInfo tempBufferInfo = new MediaCodec.BufferInfo();
- tempBufferInfo
- .set(bufferInfo.offset, bufferInfo.size, bufferInfo.presentationTimeUs,
- bufferInfo.flags);
- inputBufferInfo.add(tempBufferInfo);
- ByteBuffer tempSampleBuffer = ByteBuffer.allocate(tempBufferInfo.size);
- tempSampleBuffer.put(extractor.getFrameBuffer().array(), 0, bufferInfo.size);
- inputBuffer.add(tempSampleBuffer);
- if (sampleSize < 0) {
- break;
- }
- }
- MediaFormat format = extractor.getFormat(currentTrack);
- int outputFormat = mMapFormat.getOrDefault(mFormat, -1);
- if (outputFormat != -1) {
- Muxer muxer = new Muxer();
- int trackIndex = muxer.setUpMuxer(mContext, outputFormat, format);
- status = muxer.mux(trackIndex, inputBuffer, inputBufferInfo);
- if (status != 0) {
- Log.e(TAG, "Cannot perform write operation for " + mInputFileName);
- }
- muxer.deInitMuxer();
- muxer.dumpStatistics(mInputFileName, extractor.getClipDuration());
- muxer.resetMuxer();
- extractor.unselectExtractorTrack(currentTrack);
- inputBufferInfo.clear();
- inputBuffer.clear();
- } else {
- Log.e(TAG, "Test failed for " + mInputFileName + ". Returned invalid " +
- "output format for given " + mFormat + " format.");
+ assertTrue("Cannot find " + mInputFileName + " in directory " + mInputFilePath,
+ inputFile.exists());
+ FileInputStream fileInput = new FileInputStream(inputFile);
+ FileDescriptor fileDescriptor = fileInput.getFD();
+ ArrayList<ByteBuffer> inputBuffer = new ArrayList<>();
+ ArrayList<MediaCodec.BufferInfo> inputBufferInfo = new ArrayList<>();
+ Extractor extractor = new Extractor();
+ int trackCount = extractor.setUpExtractor(fileDescriptor);
+ for (int currentTrack = 0; currentTrack < trackCount; currentTrack++) {
+ extractor.selectExtractorTrack(currentTrack);
+ while (true) {
+ int sampleSize = extractor.getFrameSample();
+ MediaCodec.BufferInfo bufferInfo = extractor.getBufferInfo();
+ MediaCodec.BufferInfo tempBufferInfo = new MediaCodec.BufferInfo();
+ tempBufferInfo
+ .set(bufferInfo.offset, bufferInfo.size, bufferInfo.presentationTimeUs,
+ bufferInfo.flags);
+ inputBufferInfo.add(tempBufferInfo);
+ ByteBuffer tempSampleBuffer = ByteBuffer.allocate(tempBufferInfo.size);
+ tempSampleBuffer.put(extractor.getFrameBuffer().array(), 0, bufferInfo.size);
+ inputBuffer.add(tempSampleBuffer);
+ if (sampleSize < 0) {
+ break;
}
}
- extractor.deinitExtractor();
- fileInput.close();
- } else {
- Log.w(TAG, "Warning: Test Skipped. Cannot find " + mInputFileName + " in directory " +
- mInputFilePath);
+ MediaFormat format = extractor.getFormat(currentTrack);
+ int outputFormat = mMapFormat.getOrDefault(mFormat, -1);
+ assertNotEquals("Test failed for " + mInputFileName + ". Returned invalid " +
+ "output format for given " + mFormat + " format.", -1, outputFormat);
+ Muxer muxer = new Muxer();
+ int trackIndex = muxer.setUpMuxer(mContext, outputFormat, format);
+ int status = muxer.mux(trackIndex, inputBuffer, inputBufferInfo);
+ assertEquals("Cannot perform write operation for " + mInputFileName, 0, status);
+ Log.i(TAG, "Muxed " + mInputFileName + " successfully.");
+ muxer.deInitMuxer();
+ muxer.dumpStatistics(mInputFileName, mFormat, extractor.getClipDuration(), mStatsFile);
+ muxer.resetMuxer();
+ extractor.unselectExtractorTrack(currentTrack);
+ inputBufferInfo.clear();
+ inputBuffer.clear();
+
}
- assertThat(status, is(equalTo(0)));
+ extractor.deinitExtractor();
+ fileInput.close();
+ }
+
+ @Test
+ public void sampleMuxerNativeTest() {
+ Native nativeMuxer = new Native();
+ File inputFile = new File(mInputFilePath + mInputFileName);
+ assertTrue("Cannot find " + mInputFileName + " in directory " + mInputFilePath,
+ inputFile.exists());
+ int tid = android.os.Process.myTid();
+ String mMuxOutputFile = (mContext.getFilesDir() + "/mux_" + tid + ".out");
+ int status = nativeMuxer.Mux(
+ mInputFilePath, mInputFileName, mMuxOutputFile, mStatsFile, mFormat);
+ assertEquals("Cannot perform write operation for " + mInputFileName, 0, status);
+ Log.i(TAG, "Muxed " + mInputFileName + " successfully.");
+ File muxedFile = new File(mMuxOutputFile);
+ // Cleanup temporary output file
+ if (muxedFile.exists()) {
+ assertTrue("Unable to delete" + mMuxOutputFile + " file.",
+ muxedFile.delete());
+ }
}
}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/Android.bp b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/Android.bp
new file mode 100644
index 0000000..c72eb55
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/Android.bp
@@ -0,0 +1,32 @@
+cc_test_library {
+ name: "libmediabenchmark_jni",
+
+ defaults: [
+ "libmediabenchmark_common-defaults",
+ "libmediabenchmark_soft_sanitize_all-defaults",
+ ],
+
+ srcs: [
+ "NativeExtractor.cpp",
+ "NativeMuxer.cpp",
+ "NativeEncoder.cpp",
+ "NativeDecoder.cpp",
+ ],
+
+ shared_libs: [
+ "liblog",
+ ],
+
+ static_libs: [
+ "libmediabenchmark_common",
+ "libmediabenchmark_extractor",
+ "libmediabenchmark_muxer",
+ "libmediabenchmark_decoder",
+ "libmediabenchmark_encoder",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/CMakeLists.txt b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/CMakeLists.txt
new file mode 100644
index 0000000..5823883
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/CMakeLists.txt
@@ -0,0 +1,44 @@
+#
+# Copyright (C) 2019 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.
+#
+
+cmake_minimum_required(VERSION 3.4.1)
+
+set(native_source_path "../../../../src/native")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror")
+
+add_library(
+ mediabenchmark_jni SHARED
+ NativeExtractor.cpp
+ NativeMuxer.cpp
+ NativeDecoder.cpp
+ NativeEncoder.cpp
+ ${native_source_path}/common/BenchmarkCommon.cpp
+ ${native_source_path}/common/Stats.cpp
+ ${native_source_path}/common/utils/Timers.cpp
+ ${native_source_path}/extractor/Extractor.cpp
+ ${native_source_path}/muxer/Muxer.cpp
+ ${native_source_path}/decoder/Decoder.cpp
+ ${native_source_path}/encoder/Encoder.cpp)
+
+include_directories(${native_source_path}/common)
+include_directories(${native_source_path}/extractor)
+include_directories(${native_source_path}/muxer)
+include_directories(${native_source_path}/decoder)
+include_directories(${native_source_path}/encoder)
+
+find_library(log-lib log)
+
+target_link_libraries(mediabenchmark_jni mediandk ${log-lib})
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeDecoder.cpp b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeDecoder.cpp
new file mode 100644
index 0000000..043bc9e
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeDecoder.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2019 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 "NativeDecoder"
+
+#include <jni.h>
+#include <fstream>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <android/log.h>
+
+#include "Decoder.h"
+
+extern "C" JNIEXPORT int JNICALL Java_com_android_media_benchmark_library_Native_Decode(
+ JNIEnv *env, jobject thiz, jstring jFilePath, jstring jFileName, jstring jStatsFile,
+ jstring jCodecName, jboolean asyncMode) {
+ const char *filePath = env->GetStringUTFChars(jFilePath, nullptr);
+ const char *fileName = env->GetStringUTFChars(jFileName, nullptr);
+ string sFilePath = string(filePath) + string(fileName);
+ UNUSED(thiz);
+ FILE *inputFp = fopen(sFilePath.c_str(), "rb");
+ env->ReleaseStringUTFChars(jFileName, fileName);
+ env->ReleaseStringUTFChars(jFilePath, filePath);
+ if (!inputFp) {
+ ALOGE("Unable to open input file for reading");
+ return -1;
+ }
+
+ Decoder *decoder = new Decoder();
+ Extractor *extractor = decoder->getExtractor();
+ if (!extractor) {
+ ALOGE("Extractor creation failed");
+ return -1;
+ }
+
+ // Read file properties
+ struct stat buf;
+ stat(sFilePath.c_str(), &buf);
+ size_t fileSize = buf.st_size;
+ if (fileSize > kMaxBufferSize) {
+ ALOGE("File size greater than maximum buffer size");
+ return -1;
+ }
+ int32_t fd = fileno(inputFp);
+ int32_t trackCount = extractor->initExtractor(fd, fileSize);
+ if (trackCount <= 0) {
+ ALOGE("initExtractor failed");
+ return -1;
+ }
+ for (int curTrack = 0; curTrack < trackCount; curTrack++) {
+ int32_t status = extractor->setupTrackFormat(curTrack);
+ if (status != 0) {
+ ALOGE("Track Format invalid");
+ return -1;
+ }
+
+ uint8_t *inputBuffer = (uint8_t *) malloc(fileSize);
+ if (!inputBuffer) {
+ ALOGE("Insufficient memory");
+ return -1;
+ }
+
+ vector<AMediaCodecBufferInfo> frameInfo;
+ AMediaCodecBufferInfo info;
+ uint32_t inputBufferOffset = 0;
+
+ // Get frame data
+ while (1) {
+ status = extractor->getFrameSample(info);
+ if (status || !info.size) break;
+ // copy the meta data and buffer to be passed to decoder
+ if (inputBufferOffset + info.size > kMaxBufferSize) {
+ ALOGE("Memory allocated not sufficient");
+ free(inputBuffer);
+ return -1;
+ }
+ memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(), info.size);
+ frameInfo.push_back(info);
+ inputBufferOffset += info.size;
+ }
+
+ const char *codecName = env->GetStringUTFChars(jCodecName, nullptr);
+ string sCodecName = string(codecName);
+ decoder->setupDecoder();
+ status = decoder->decode(inputBuffer, frameInfo, sCodecName, asyncMode);
+ if (status != AMEDIA_OK) {
+ ALOGE("Decode returned error");
+ free(inputBuffer);
+ env->ReleaseStringUTFChars(jCodecName, codecName);
+ return -1;
+ }
+ decoder->deInitCodec();
+ const char *inputReference = env->GetStringUTFChars(jFileName, nullptr);
+ const char *statsFile = env->GetStringUTFChars(jStatsFile, nullptr);
+ string sInputReference = string(inputReference);
+ decoder->dumpStatistics(sInputReference, sCodecName, (asyncMode ? "async" : "sync"),
+ statsFile);
+ env->ReleaseStringUTFChars(jCodecName, codecName);
+ env->ReleaseStringUTFChars(jStatsFile, statsFile);
+ env->ReleaseStringUTFChars(jFileName, inputReference);
+ if (inputBuffer) {
+ free(inputBuffer);
+ inputBuffer = nullptr;
+ }
+ decoder->resetDecoder();
+ }
+ if (inputFp) {
+ fclose(inputFp);
+ inputFp = nullptr;
+ }
+ extractor->deInitExtractor();
+ delete decoder;
+ return 0;
+}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeEncoder.cpp b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeEncoder.cpp
new file mode 100644
index 0000000..271b852
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeEncoder.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2019 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 "NativeEncoder"
+
+#include <jni.h>
+#include <fstream>
+#include <iostream>
+#include <sys/stat.h>
+
+#include <android/log.h>
+
+#include "Decoder.h"
+#include "Encoder.h"
+
+#include <stdio.h>
+
+extern "C" JNIEXPORT int JNICALL Java_com_android_media_benchmark_library_Native_Encode(
+ JNIEnv *env, jobject thiz, jstring jFilePath, jstring jFileName, jstring jOutFilePath,
+ jstring jStatsFile, jstring jCodecName) {
+ const char *filePath = env->GetStringUTFChars(jFilePath, nullptr);
+ const char *fileName = env->GetStringUTFChars(jFileName, nullptr);
+ string sFilePath = string(filePath) + string(fileName);
+ UNUSED(thiz);
+ FILE *inputFp = fopen(sFilePath.c_str(), "rb");
+ env->ReleaseStringUTFChars(jFileName, fileName);
+ env->ReleaseStringUTFChars(jFilePath, filePath);
+ if (!inputFp) {
+ ALOGE("Unable to open input file for reading");
+ return -1;
+ }
+
+ Decoder *decoder = new Decoder();
+ Extractor *extractor = decoder->getExtractor();
+ if (!extractor) {
+ ALOGE("Extractor creation failed");
+ return -1;
+ }
+
+ // Read file properties
+ struct stat buf;
+ stat(sFilePath.c_str(), &buf);
+ size_t fileSize = buf.st_size;
+ if (fileSize > kMaxBufferSize) {
+ ALOGE("File size greater than maximum buffer size");
+ return -1;
+ }
+ int32_t fd = fileno(inputFp);
+ int32_t trackCount = extractor->initExtractor(fd, fileSize);
+ if (trackCount <= 0) {
+ ALOGE("initExtractor failed");
+ return -1;
+ }
+
+ for (int curTrack = 0; curTrack < trackCount; curTrack++) {
+ int32_t status = extractor->setupTrackFormat(curTrack);
+ if (status != 0) {
+ ALOGE("Track Format invalid");
+ return -1;
+ }
+ uint8_t *inputBuffer = (uint8_t *) malloc(fileSize);
+ if (!inputBuffer) {
+ ALOGE("Insufficient memory");
+ return -1;
+ }
+ vector<AMediaCodecBufferInfo> frameInfo;
+ AMediaCodecBufferInfo info;
+ uint32_t inputBufferOffset = 0;
+
+ // Get frame data
+ while (1) {
+ status = extractor->getFrameSample(info);
+ if (status || !info.size) break;
+ // copy the meta data and buffer to be passed to decoder
+ if (inputBufferOffset + info.size > kMaxBufferSize) {
+ ALOGE("Memory allocated not sufficient");
+ free(inputBuffer);
+ return -1;
+ }
+ memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(), info.size);
+ frameInfo.push_back(info);
+ inputBufferOffset += info.size;
+ }
+ string decName = "";
+ const char *outputFilePath = env->GetStringUTFChars(jOutFilePath, nullptr);
+ FILE *outFp = fopen(outputFilePath, "wb");
+ if (outFp == nullptr) {
+ ALOGE("%s - File failed to open for writing!", outputFilePath);
+ free(inputBuffer);
+ return -1;
+ }
+ decoder->setupDecoder();
+ status = decoder->decode(inputBuffer, frameInfo, decName, false /*asyncMode */, outFp);
+ if (status != AMEDIA_OK) {
+ ALOGE("Decode returned error");
+ free(inputBuffer);
+ return -1;
+ }
+ AMediaFormat *format = extractor->getFormat();
+ if (inputBuffer) {
+ free(inputBuffer);
+ inputBuffer = nullptr;
+ }
+ const char *mime = nullptr;
+ AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime);
+ if (!mime) {
+ ALOGE("Error in AMediaFormat_getString");
+ return -1;
+ }
+ ifstream eleStream;
+ eleStream.open(outputFilePath, ifstream::binary | ifstream::ate);
+ if (!eleStream.is_open()) {
+ ALOGE("%s - File failed to open for reading!", outputFilePath);
+ env->ReleaseStringUTFChars(jOutFilePath, outputFilePath);
+ return -1;
+ }
+ const char *codecName = env->GetStringUTFChars(jCodecName, NULL);
+ const char *inputReference = env->GetStringUTFChars(jFileName, nullptr);
+ string sCodecName = string(codecName);
+ string sInputReference = string(inputReference);
+
+ bool asyncMode[2] = {true, false};
+ for (int i = 0; i < 2; i++) {
+ size_t eleSize = eleStream.tellg();
+ eleStream.seekg(0, ifstream::beg);
+
+ // Get encoder params
+ encParameter encParams;
+ if (!strncmp(mime, "video/", 6)) {
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &encParams.width);
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &encParams.height);
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_FRAME_RATE, &encParams.frameRate);
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, &encParams.bitrate);
+ if (encParams.bitrate <= 0 || encParams.frameRate <= 0) {
+ encParams.frameRate = 25;
+ if (!strcmp(mime, "video/3gpp") || !strcmp(mime, "video/mp4v-es")) {
+ encParams.bitrate = 600000 /* 600 Kbps */;
+ } else {
+ encParams.bitrate = 8000000 /* 8 Mbps */;
+ }
+ }
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_PROFILE, &encParams.profile);
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_LEVEL, &encParams.level);
+ } else {
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &encParams.sampleRate);
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT,
+ &encParams.numChannels);
+ encParams.bitrate =
+ encParams.sampleRate * encParams.numChannels * 16 /* bitsPerSample */;
+ }
+ Encoder *encoder = new Encoder();
+ encoder->setupEncoder();
+ status = encoder->encode(sCodecName, eleStream, eleSize, asyncMode[i], encParams,
+ (char *) mime);
+ encoder->deInitCodec();
+ cout << "codec : " << codecName << endl;
+ ALOGV(" asyncMode = %d \n", asyncMode[i]);
+ const char *statsFile = env->GetStringUTFChars(jStatsFile, nullptr);
+ encoder->dumpStatistics(sInputReference, extractor->getClipDuration(), sCodecName,
+ (asyncMode[i] ? "async" : "sync"), statsFile);
+ env->ReleaseStringUTFChars(jStatsFile, statsFile);
+ encoder->resetEncoder();
+ delete encoder;
+ encoder = nullptr;
+ }
+ eleStream.close();
+ if (outFp) {
+ fclose(outFp);
+ outFp = nullptr;
+ }
+ env->ReleaseStringUTFChars(jFileName, inputReference);
+ env->ReleaseStringUTFChars(jCodecName, codecName);
+ env->ReleaseStringUTFChars(jOutFilePath, outputFilePath);
+ if (format) {
+ AMediaFormat_delete(format);
+ format = nullptr;
+ }
+ decoder->deInitCodec();
+ decoder->resetDecoder();
+ }
+ if (inputFp) {
+ fclose(inputFp);
+ inputFp = nullptr;
+ }
+ extractor->deInitExtractor();
+ delete decoder;
+ return 0;
+}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeExtractor.cpp b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeExtractor.cpp
new file mode 100644
index 0000000..a762760
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeExtractor.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2019 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 "NativeExtractor"
+
+#include <jni.h>
+#include <fstream>
+#include <string>
+#include <sys/stat.h>
+
+#include "Extractor.h"
+
+extern "C" JNIEXPORT int32_t JNICALL Java_com_android_media_benchmark_library_Native_Extract(
+ JNIEnv *env, jobject thiz, jstring jInputFilePath, jstring jInputFileName,
+ jstring jStatsFile) {
+ UNUSED(thiz);
+ const char *inputFilePath = env->GetStringUTFChars(jInputFilePath, nullptr);
+ const char *inputFileName = env->GetStringUTFChars(jInputFileName, nullptr);
+ string sFilePath = string(inputFilePath) + string(inputFileName);
+ FILE *inputFp = fopen(sFilePath.c_str(), "rb");
+
+ // Read file properties
+ struct stat buf;
+ stat(sFilePath.c_str(), &buf);
+ size_t fileSize = buf.st_size;
+ int32_t fd = fileno(inputFp);
+
+ Extractor *extractObj = new Extractor();
+ int32_t trackCount = extractObj->initExtractor((long) fd, fileSize);
+ if (trackCount <= 0) {
+ ALOGE("initExtractor failed");
+ return -1;
+ }
+
+ int32_t trackID = 0;
+ const char *mime = nullptr;
+ int32_t status = extractObj->extract(trackID);
+ if (status != AMEDIA_OK) {
+ ALOGE("Extraction failed");
+ return -1;
+ }
+
+ if (inputFp) {
+ fclose(inputFp);
+ inputFp = nullptr;
+ }
+ status = extractObj->setupTrackFormat(trackID);
+ AMediaFormat *format = extractObj->getFormat();
+ if (!format) {
+ ALOGE("format is null!");
+ return -1;
+ }
+ AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime);
+ if (!mime) {
+ ALOGE("mime is null!");
+ return -1;
+ }
+ extractObj->deInitExtractor();
+ const char *statsFile = env->GetStringUTFChars(jStatsFile, nullptr);
+ extractObj->dumpStatistics(string(inputFileName), string(mime), statsFile);
+ env->ReleaseStringUTFChars(jStatsFile, statsFile);
+ env->ReleaseStringUTFChars(jInputFilePath, inputFilePath);
+ env->ReleaseStringUTFChars(jInputFileName, inputFileName);
+
+ delete extractObj;
+ return status;
+}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeMuxer.cpp b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeMuxer.cpp
new file mode 100644
index 0000000..a5ef5b8
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeMuxer.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2019 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 "NativeMuxer"
+
+#include <jni.h>
+#include <fstream>
+#include <string>
+#include <sys/stat.h>
+
+#include "Muxer.h"
+
+MUXER_OUTPUT_T getMuxerOutFormat(const char *fmt);
+
+extern "C" JNIEXPORT int32_t JNICALL Java_com_android_media_benchmark_library_Native_Mux(
+ JNIEnv *env, jobject thiz, jstring jInputFilePath, jstring jInputFileName,
+ jstring jOutputFilePath, jstring jStatsFile, jstring jFormat) {
+ UNUSED(thiz);
+ ALOGV("Mux the samples given by extractor");
+ const char *inputFilePath = env->GetStringUTFChars(jInputFilePath, nullptr);
+ const char *inputFileName = env->GetStringUTFChars(jInputFileName, nullptr);
+ string sInputFile = string(inputFilePath) + string(inputFileName);
+ FILE *inputFp = fopen(sInputFile.c_str(), "rb");
+ if (!inputFp) {
+ ALOGE("Unable to open input file for reading");
+ return -1;
+ }
+
+ const char *fmt = env->GetStringUTFChars(jFormat, nullptr);
+ MUXER_OUTPUT_T outputFormat = getMuxerOutFormat(fmt);
+ if (outputFormat == MUXER_OUTPUT_FORMAT_INVALID) {
+ ALOGE("output format is MUXER_OUTPUT_FORMAT_INVALID");
+ return MUXER_OUTPUT_FORMAT_INVALID;
+ }
+
+ Muxer *muxerObj = new Muxer();
+ Extractor *extractor = muxerObj->getExtractor();
+ if (!extractor) {
+ ALOGE("Extractor creation failed");
+ return -1;
+ }
+
+ // Read file properties
+ struct stat buf;
+ stat(sInputFile.c_str(), &buf);
+ size_t fileSize = buf.st_size;
+ int32_t fd = fileno(inputFp);
+
+ int32_t trackCount = extractor->initExtractor(fd, fileSize);
+ if (trackCount <= 0) {
+ ALOGE("initExtractor failed");
+ return -1;
+ }
+
+ for (int curTrack = 0; curTrack < trackCount; curTrack++) {
+ int32_t status = extractor->setupTrackFormat(curTrack);
+ if (status != 0) {
+ ALOGE("Track Format invalid");
+ return -1;
+ }
+
+ uint8_t *inputBuffer = (uint8_t *) malloc(fileSize);
+ if (!inputBuffer) {
+ ALOGE("Allocation Failed");
+ return -1;
+ }
+ vector<AMediaCodecBufferInfo> frameInfos;
+ AMediaCodecBufferInfo info;
+ uint32_t inputBufferOffset = 0;
+
+ // Get Frame Data
+ while (1) {
+ status = extractor->getFrameSample(info);
+ if (status || !info.size) break;
+ // copy the meta data and buffer to be passed to muxer
+ if (inputBufferOffset + info.size > fileSize) {
+ ALOGE("Memory allocated not sufficient");
+ if (inputBuffer) {
+ free(inputBuffer);
+ inputBuffer = nullptr;
+ }
+ return -1;
+ }
+ memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(),
+ static_cast<size_t>(info.size));
+ info.offset = inputBufferOffset;
+ frameInfos.push_back(info);
+ inputBufferOffset += info.size;
+ }
+
+ const char *outputFilePath = env->GetStringUTFChars(jOutputFilePath, nullptr);
+ FILE *outputFp = fopen(((string) outputFilePath).c_str(), "w+b");
+ env->ReleaseStringUTFChars(jOutputFilePath, outputFilePath);
+
+ if (!outputFp) {
+ ALOGE("Unable to open output file for writing");
+ if (inputBuffer) {
+ free(inputBuffer);
+ inputBuffer = nullptr;
+ }
+ return -1;
+ }
+ int32_t outFd = fileno(outputFp);
+
+ status = muxerObj->initMuxer(outFd, (MUXER_OUTPUT_T) outputFormat);
+ if (status != 0) {
+ ALOGE("initMuxer failed");
+ if (inputBuffer) {
+ free(inputBuffer);
+ inputBuffer = nullptr;
+ }
+ return -1;
+ }
+
+ status = muxerObj->mux(inputBuffer, frameInfos);
+ if (status != 0) {
+ ALOGE("Mux failed");
+ if (inputBuffer) {
+ free(inputBuffer);
+ inputBuffer = nullptr;
+ }
+ return -1;
+ }
+ muxerObj->deInitMuxer();
+ const char *statsFile = env->GetStringUTFChars(jStatsFile, nullptr);
+ string muxFormat(fmt);
+ muxerObj->dumpStatistics(string(inputFileName), muxFormat, statsFile);
+ env->ReleaseStringUTFChars(jStatsFile, statsFile);
+ env->ReleaseStringUTFChars(jInputFilePath, inputFilePath);
+ env->ReleaseStringUTFChars(jInputFileName, inputFileName);
+
+ if (inputBuffer) {
+ free(inputBuffer);
+ inputBuffer = nullptr;
+ }
+ if (outputFp) {
+ fclose(outputFp);
+ outputFp = nullptr;
+ }
+ muxerObj->resetMuxer();
+ }
+ if (inputFp) {
+ fclose(inputFp);
+ inputFp = nullptr;
+ }
+ env->ReleaseStringUTFChars(jFormat, fmt);
+ extractor->deInitExtractor();
+ delete muxerObj;
+
+ return 0;
+}
+
+MUXER_OUTPUT_T getMuxerOutFormat(const char *fmt) {
+ static const struct {
+ const char *name;
+ int value;
+ } kFormatMaps[] = {{"mp4", MUXER_OUTPUT_FORMAT_MPEG_4},
+ {"webm", MUXER_OUTPUT_FORMAT_WEBM},
+ {"3gpp", MUXER_OUTPUT_FORMAT_3GPP},
+ {"ogg", MUXER_OUTPUT_FORMAT_OGG}};
+
+ int32_t muxOutputFormat = MUXER_OUTPUT_FORMAT_INVALID;
+ for (auto kFormatMap : kFormatMaps) {
+ if (!strcmp(fmt, kFormatMap.name)) {
+ muxOutputFormat = kFormatMap.value;
+ break;
+ }
+ }
+ return (MUXER_OUTPUT_T) muxOutputFormat;
+}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java
index 2cd27c2..3b1eed4 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java
@@ -239,11 +239,16 @@
* Prints out the statistics in the information log
*
* @param inputReference The operation being performed, in this case decode
+ * @param componentName Name of the component/codec
+ * @param mode The operating mode: Sync/Async
* @param durationUs Duration of the clip in microseconds
+ * @param statsFile The output file where the stats data is written
*/
- public void dumpStatistics(String inputReference, long durationUs) {
+ public void dumpStatistics(String inputReference, String componentName, String mode,
+ long durationUs, String statsFile) throws IOException {
String operation = "decode";
- mStats.dumpStatistics(operation, inputReference, durationUs);
+ mStats.dumpStatistics(
+ inputReference, operation, componentName, mode, durationUs, statsFile);
}
/**
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java
index 03db294..40cf8bd 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java
@@ -326,12 +326,17 @@
/**
* Prints out the statistics in the information log
*
- * @param inputReference The operation being performed, in this case encode
+ * @param inputReference The operation being performed, in this case decode
+ * @param componentName Name of the component/codec
+ * @param mode The operating mode: Sync/Async
* @param durationUs Duration of the clip in microseconds
+ * @param statsFile The output file where the stats data is written
*/
- public void dumpStatistics(String inputReference, long durationUs) {
+ public void dumpStatistics(String inputReference, String componentName, String mode,
+ long durationUs, String statsFile) throws IOException {
String operation = "encode";
- mStats.dumpStatistics(operation, inputReference, durationUs);
+ mStats.dumpStatistics(
+ inputReference, operation, componentName, mode, durationUs, statsFile);
}
/**
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Extractor.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Extractor.java
index 459e2a9..f3024e7 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Extractor.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Extractor.java
@@ -167,9 +167,12 @@
* Write the benchmark logs for the given input file
*
* @param inputReference Name of the input file
+ * @param mimeType Mime type of the muxed file
+ * @param statsFile The output file where the stats data is written
*/
- public void dumpStatistics(String inputReference) {
+ public void dumpStatistics(String inputReference, String mimeType, String statsFile)
+ throws IOException {
String operation = "extract";
- mStats.dumpStatistics(operation, inputReference, mDurationUs);
+ mStats.dumpStatistics(inputReference, operation, mimeType, "", mDurationUs, statsFile);
}
}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Muxer.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Muxer.java
index 49eaa1c..340b539 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Muxer.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Muxer.java
@@ -101,10 +101,13 @@
* Write the benchmark logs for the given input file
*
* @param inputReference Name of the input file
+ * @param muxFormat Format of the muxed output
* @param clipDuration Duration of the given inputReference file
+ * @param statsFile The output file where the stats data is written
*/
- public void dumpStatistics(String inputReference, long clipDuration) {
+ public void dumpStatistics(String inputReference, String muxFormat, long clipDuration,
+ String statsFile) throws IOException {
String operation = "mux";
- mStats.dumpStatistics(operation, inputReference, clipDuration);
+ mStats.dumpStatistics(inputReference, operation, muxFormat, "", clipDuration, statsFile);
}
}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Native.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Native.java
new file mode 100644
index 0000000..38b608a
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Native.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media.benchmark.library;
+
+public class Native {
+ static { System.loadLibrary("mediabenchmark_jni"); }
+
+ public native int Extract(String inputFilePath, String inputFileName, String statsFile);
+
+ public native int Mux(String inputFilePath, String inputFileName, String outputFilePath,
+ String statsFile, String format);
+
+ public native int Decode(String inputFilePath, String inputFileName, String statsFile,
+ String codecName, boolean asyncMode);
+
+ public native int Encode(String inputFilePath, String inputFileName, String outputFilePath,
+ String statsFile, String codecName);
+}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Stats.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Stats.java
index 18ab5be..7245a3a 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Stats.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Stats.java
@@ -18,6 +18,10 @@
import android.util.Log;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
import java.util.ArrayList;
/**
@@ -91,14 +95,38 @@
}
/**
+ * Writes the stats header to a file
+ * <p>
+ * \param statsFile file where the stats data is to be written
+ **/
+ public boolean writeStatsHeader(String statsFile) throws IOException {
+ File outputFile = new File(statsFile);
+ FileOutputStream out = new FileOutputStream(outputFile, true);
+ if (!outputFile.exists())
+ return false;
+ String statsHeader =
+ "currentTime, fileName, operation, componentName, NDK/SDK, sync/async, setupTime, "
+ + "destroyTime, minimumTime, maximumTime, "
+ + "averageTime, timeToProcess1SecContent, totalBytesProcessedPerSec, "
+ + "timeToFirstFrame, totalSizeInBytes, totalTime\n";
+ out.write(statsHeader.getBytes());
+ out.close();
+ return true;
+ }
+
+ /**
* Dumps the stats of the operation for a given input media.
* <p>
+ * \param inputReference input media
* \param operation describes the operation performed on the input media
* (i.e. extract/mux/decode/encode)
- * \param inputReference input media
- * \param durationUs is a duration of the input media in microseconds.
+ * \param componentName name of the codec/muxFormat/mime
+ * \param mode the operating mode: sync/async.
+ * \param durationUs is a duration of the input media in microseconds.
+ * \param statsFile the file where the stats data is to be written.
*/
- public void dumpStatistics(String operation, String inputReference, long durationUs) {
+ public void dumpStatistics(String inputReference, String operation, String componentName,
+ String mode, long durationUs, String statsFile) throws IOException {
if (mOutputTimer.size() == 0) {
Log.e(TAG, "No output produced");
return;
@@ -121,18 +149,30 @@
maxTimeTakenNs = intervalNs;
}
}
- // Print the Stats
- Log.i(TAG, "Input Reference : " + inputReference);
- Log.i(TAG, "Setup Time in nano sec : " + mInitTimeNs);
- Log.i(TAG, "Average Time in nano sec : " + totalTimeTakenNs / mOutputTimer.size());
- Log.i(TAG, "Time to first frame in nano sec : " + timeToFirstFrameNs);
- Log.i(TAG, "Time taken (in nano sec) to " + operation + " 1 sec of content : " +
- timeTakenPerSec);
- Log.i(TAG, "Total bytes " + operation + "ed : " + size);
- Log.i(TAG, "Number of bytes " + operation + "ed per second : " +
- (size * 1000000000) / totalTimeTakenNs);
- Log.i(TAG, "Minimum Time in nano sec : " + minTimeTakenNs);
- Log.i(TAG, "Maximum Time in nano sec : " + maxTimeTakenNs);
- Log.i(TAG, "Destroy Time in nano sec : " + mDeInitTimeNs);
+
+ // Write the stats row data to file
+ String rowData = "";
+ rowData += System.nanoTime() + ", ";
+ rowData += inputReference + ", ";
+ rowData += operation + ", ";
+ rowData += componentName + ", ";
+ rowData += "SDK, ";
+ rowData += mode + ", ";
+ rowData += mInitTimeNs + ", ";
+ rowData += mDeInitTimeNs + ", ";
+ rowData += minTimeTakenNs + ", ";
+ rowData += maxTimeTakenNs + ", ";
+ rowData += totalTimeTakenNs / mOutputTimer.size() + ", ";
+ rowData += timeTakenPerSec + ", ";
+ rowData += (size * 1000000000) / totalTimeTakenNs + ", ";
+ rowData += timeToFirstFrameNs + ", ";
+ rowData += size + ", ";
+ rowData += totalTimeTakenNs + "\n";
+
+ File outputFile = new File(statsFile);
+ FileOutputStream out = new FileOutputStream(outputFile, true);
+ assert outputFile.exists() : "Failed to open the stats file for writing!";
+ out.write(rowData.getBytes());
+ out.close();
}
-}
\ No newline at end of file
+}
diff --git a/media/tests/benchmark/README.md b/media/tests/benchmark/README.md
index 6627462..05fbe6f 100644
--- a/media/tests/benchmark/README.md
+++ b/media/tests/benchmark/README.md
@@ -145,3 +145,12 @@
```
adb shell /data/local/tmp/C2DecoderTest -P /data/local/tmp/MediaBenchmark/res/
```
+## C2 Encoder
+
+The test encodes input stream and benchmarks the codec2 encoders available in device.
+
+Setup steps are same as [extractor](#extractor).
+
+```
+adb shell /data/local/tmp/C2EncoderTest -P /data/local/tmp/MediaBenchmark/res/
+```
diff --git a/media/tests/benchmark/src/native/common/Android.bp b/media/tests/benchmark/src/native/common/Android.bp
index babc329..f8ea25c 100644
--- a/media/tests/benchmark/src/native/common/Android.bp
+++ b/media/tests/benchmark/src/native/common/Android.bp
@@ -29,7 +29,7 @@
export_include_dirs: ["."],
- ldflags: ["-Wl,-Bsymbolic"]
+ ldflags: ["-Wl,-Bsymbolic"],
}
cc_defaults {
@@ -55,7 +55,7 @@
cflags: [
"-Wall",
"-Werror",
- ]
+ ],
}
cc_library_static {
@@ -66,18 +66,20 @@
srcs: [
"BenchmarkC2Common.cpp",
+ "BenchmarkCommon.cpp",
+ "Stats.cpp",
+ "utils/Timers.cpp",
],
export_include_dirs: ["."],
- ldflags: ["-Wl,-Bsymbolic"]
+ ldflags: ["-Wl,-Bsymbolic"],
}
cc_defaults {
name: "libmediabenchmark_codec2_common-defaults",
defaults: [
- "libmediabenchmark_common-defaults",
"libcodec2-hidl-client-defaults",
"libmediabenchmark_soft_sanitize_all-defaults",
],
@@ -88,7 +90,14 @@
shared_libs: [
"libcodec2_client",
- ]
+ "libmediandk",
+ "liblog",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
// public dependency for native implementation
@@ -102,5 +111,5 @@
"signed-integer-overflow",
],
cfi: true,
- }
+ },
}
diff --git a/media/tests/benchmark/src/native/common/BenchmarkC2Common.cpp b/media/tests/benchmark/src/native/common/BenchmarkC2Common.cpp
index 622a0e1..e09f468 100644
--- a/media/tests/benchmark/src/native/common/BenchmarkC2Common.cpp
+++ b/media/tests/benchmark/src/native/common/BenchmarkC2Common.cpp
@@ -22,6 +22,9 @@
int32_t BenchmarkC2Common::setupCodec2() {
ALOGV("In %s", __func__);
mClient = android::Codec2Client::CreateFromService("default");
+ if (!mClient) {
+ mClient = android::Codec2Client::CreateFromService("software");
+ }
if (!mClient) return -1;
std::shared_ptr<C2AllocatorStore> store = android::GetCodec2PlatformAllocatorStore();
diff --git a/media/tests/benchmark/src/native/common/BenchmarkCommon.cpp b/media/tests/benchmark/src/native/common/BenchmarkCommon.cpp
index ab74508..cb49b8e 100644
--- a/media/tests/benchmark/src/native/common/BenchmarkCommon.cpp
+++ b/media/tests/benchmark/src/native/common/BenchmarkCommon.cpp
@@ -56,6 +56,7 @@
void OnErrorCB(AMediaCodec *codec, void *userdata, media_status_t err, int32_t actionCode,
const char *detail) {
+ (void)codec;
ALOGE("OnErrorCB: err(%d), actionCode(%d), detail(%s)", err, actionCode, detail);
CallBackHandle *self = (CallBackHandle *)userdata;
self->mSawError = true;
@@ -91,7 +92,7 @@
/* Configure codec with the given format*/
const char *s = AMediaFormat_toString(format);
- ALOGV("Input format: %s\n", s);
+ ALOGI("Input format: %s\n", s);
media_status_t status = AMediaCodec_configure(codec, format, nullptr, nullptr, isEncoder);
if (status != AMEDIA_OK) {
@@ -99,4 +100,4 @@
return nullptr;
}
return codec;
-}
\ No newline at end of file
+}
diff --git a/media/tests/benchmark/src/native/common/BenchmarkCommon.h b/media/tests/benchmark/src/native/common/BenchmarkCommon.h
index 8153a86..c11fe36 100644
--- a/media/tests/benchmark/src/native/common/BenchmarkCommon.h
+++ b/media/tests/benchmark/src/native/common/BenchmarkCommon.h
@@ -17,15 +17,18 @@
#ifndef __BENCHMARK_COMMON_H__
#define __BENCHMARK_COMMON_H__
+#include <sys/stat.h>
#include <inttypes.h>
#include <mutex>
#include <queue>
#include <thread>
+#include <iostream>
#include <media/NdkMediaCodec.h>
#include <media/NdkMediaError.h>
#include "Stats.h"
+#define UNUSED(x) (void)(x)
using namespace std;
diff --git a/media/tests/benchmark/src/native/common/Stats.cpp b/media/tests/benchmark/src/native/common/Stats.cpp
index 2d9bb31..bfde125 100644
--- a/media/tests/benchmark/src/native/common/Stats.cpp
+++ b/media/tests/benchmark/src/native/common/Stats.cpp
@@ -17,8 +17,10 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "Stats"
+#include <ctime>
#include <iostream>
#include <stdint.h>
+#include <fstream>
#include "Stats.h"
@@ -28,16 +30,20 @@
* \param operation describes the operation performed on the input media
* (i.e. extract/mux/decode/encode)
* \param inputReference input media
- * \param duarationUs is a duration of the input media in microseconds.
+ * \param durationUs is a duration of the input media in microseconds.
+ * \param componentName describes the codecName/muxFormat/mimeType.
+ * \param mode the operating mode: sync/async.
+ * \param statsFile the file where the stats data is to be written.
*/
-void Stats::dumpStatistics(std::string operation, std::string inputReference, int64_t duarationUs) {
+void Stats::dumpStatistics(string operation, string inputReference, int64_t durationUs,
+ string componentName, string mode, string statsFile) {
ALOGV("In %s", __func__);
if (!mOutputTimer.size()) {
ALOGE("No output produced");
return;
}
nsecs_t totalTimeTakenNs = getTotalTime();
- nsecs_t timeTakenPerSec = (totalTimeTakenNs * 1000000) / duarationUs;
+ nsecs_t timeTakenPerSec = (totalTimeTakenNs * 1000000) / durationUs;
nsecs_t timeToFirstFrameNs = *mOutputTimer.begin() - mStartTimeNs;
int32_t size = std::accumulate(mFrameSizes.begin(), mFrameSizes.end(), 0);
// get min and max output intervals.
@@ -52,15 +58,32 @@
else if (maxTimeTakenNs < intervalNs) maxTimeTakenNs = intervalNs;
}
- // Print the Stats
- std::cout << "Input Reference : " << inputReference << endl;
- std::cout << "Setup Time in nano sec : " << mInitTimeNs << endl;
- std::cout << "Average Time in nano sec : " << totalTimeTakenNs / mOutputTimer.size() << endl;
- std::cout << "Time to first frame in nano sec : " << timeToFirstFrameNs << endl;
- std::cout << "Time taken (in nano sec) to " << operation
- << " 1 sec of content : " << timeTakenPerSec << endl;
- std::cout << "Total bytes " << operation << "ed : " << size << endl;
- std::cout << "Minimum Time in nano sec : " << minTimeTakenNs << endl;
- std::cout << "Maximum Time in nano sec : " << maxTimeTakenNs << endl;
- std::cout << "Destroy Time in nano sec : " << mDeInitTimeNs << endl;
+ // Write the stats data to file.
+ int64_t dataSize = size;
+ int64_t bytesPerSec = ((int64_t)dataSize * 1000000000) / totalTimeTakenNs;
+ string rowData = "";
+ rowData.append(to_string(systemTime(CLOCK_MONOTONIC)) + ", ");
+ rowData.append(inputReference + ", ");
+ rowData.append(operation + ", ");
+ rowData.append(componentName + ", ");
+ rowData.append("NDK, ");
+ rowData.append(mode + ", ");
+ rowData.append(to_string(mInitTimeNs) + ", ");
+ rowData.append(to_string(mDeInitTimeNs) + ", ");
+ rowData.append(to_string(minTimeTakenNs) + ", ");
+ rowData.append(to_string(maxTimeTakenNs) + ", ");
+ rowData.append(to_string(totalTimeTakenNs / mOutputTimer.size()) + ", ");
+ rowData.append(to_string(timeTakenPerSec) + ", ");
+ rowData.append(to_string(bytesPerSec) + ", ");
+ rowData.append(to_string(timeToFirstFrameNs) + ", ");
+ rowData.append(to_string(size) + ",");
+ rowData.append(to_string(totalTimeTakenNs) + ",\n");
+
+ ofstream out(statsFile, ios::out | ios::app);
+ if(out.bad()) {
+ ALOGE("Failed to open stats file for writing!");
+ return;
+ }
+ out << rowData;
+ out.close();
}
diff --git a/media/tests/benchmark/src/native/common/Stats.h b/media/tests/benchmark/src/native/common/Stats.h
index 2f556ee..18e4b06 100644
--- a/media/tests/benchmark/src/native/common/Stats.h
+++ b/media/tests/benchmark/src/native/common/Stats.h
@@ -18,6 +18,7 @@
#define __STATS_H__
#include <android/log.h>
+#include <inttypes.h>
#ifndef ALOG
#define ALOG(priority, tag, ...) ((void)__android_log_print(ANDROID_##priority, tag, __VA_ARGS__))
@@ -25,6 +26,12 @@
#define ALOGI(...) ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)
#define ALOGE(...) ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define ALOGD(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+#define ALOGW(...) ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)
+
+#ifndef LOG_NDEBUG
+#define LOG_NDEBUG 1
+#endif
+
#if LOG_NDEBUG
#define ALOGV(cond, ...) ((void)0)
#else
@@ -95,7 +102,8 @@
return (*(mOutputTimer.end() - 1) - mStartTimeNs);
}
- void dumpStatistics(std::string operation, std::string inputReference, int64_t duarationUs);
+ void dumpStatistics(string operation, string inputReference, int64_t duarationUs,
+ string codecName = "", string mode = "", string statsFile = "");
};
#endif // __STATS_H__
diff --git a/media/tests/benchmark/src/native/decoder/Android.bp b/media/tests/benchmark/src/native/decoder/Android.bp
index b5072ab..9791c11 100644
--- a/media/tests/benchmark/src/native/decoder/Android.bp
+++ b/media/tests/benchmark/src/native/decoder/Android.bp
@@ -27,24 +27,26 @@
export_include_dirs: ["."],
- ldflags: ["-Wl,-Bsymbolic"]
+ ldflags: ["-Wl,-Bsymbolic"],
}
cc_library_static {
name: "libmediabenchmark_codec2_decoder",
defaults: [
- "libmediabenchmark_common-defaults",
"libmediabenchmark_codec2_common-defaults",
],
- srcs: ["C2Decoder.cpp"],
+ srcs: [
+ "C2Decoder.cpp",
+ "Decoder.cpp",
+ ],
static_libs: [
"libmediabenchmark_codec2_common",
- "libmediabenchmark_extractor",
+ "libmediabenchmark_codec2_extractor",
],
export_include_dirs: ["."],
- ldflags: ["-Wl,-Bsymbolic"]
+ ldflags: ["-Wl,-Bsymbolic"],
}
diff --git a/media/tests/benchmark/src/native/decoder/Decoder.cpp b/media/tests/benchmark/src/native/decoder/Decoder.cpp
index ac0d525..2171589 100644
--- a/media/tests/benchmark/src/native/decoder/Decoder.cpp
+++ b/media/tests/benchmark/src/native/decoder/Decoder.cpp
@@ -225,12 +225,12 @@
}
void Decoder::deInitCodec() {
- int64_t sTime = mStats->getCurTime();
if (mFormat) {
AMediaFormat_delete(mFormat);
mFormat = nullptr;
}
if (!mCodec) return;
+ int64_t sTime = mStats->getCurTime();
AMediaCodec_stop(mCodec);
AMediaCodec_delete(mCodec);
int64_t eTime = mStats->getCurTime();
@@ -238,10 +238,11 @@
mStats->setDeInitTime(timeTaken);
}
-void Decoder::dumpStatistics(string inputReference) {
+void Decoder::dumpStatistics(string inputReference, string componentName, string mode,
+ string statsFile) {
int64_t durationUs = mExtractor->getClipDuration();
string operation = "decode";
- mStats->dumpStatistics(operation, inputReference, durationUs);
+ mStats->dumpStatistics(operation, inputReference, durationUs, componentName, mode, statsFile);
}
void Decoder::resetDecoder() {
diff --git a/media/tests/benchmark/src/native/decoder/Decoder.h b/media/tests/benchmark/src/native/decoder/Decoder.h
index aeda080..f3fa6a1 100644
--- a/media/tests/benchmark/src/native/decoder/Decoder.h
+++ b/media/tests/benchmark/src/native/decoder/Decoder.h
@@ -71,7 +71,8 @@
int32_t decode(uint8_t *inputBuffer, vector<AMediaCodecBufferInfo> &frameInfo,
string &codecName, bool asyncMode, FILE *outFp = nullptr);
- void dumpStatistics(string inputReference);
+ void dumpStatistics(string inputReference, string componentName = "", string mode = "",
+ string statsFile = "");
private:
AMediaCodec *mCodec;
diff --git a/media/tests/benchmark/src/native/encoder/Android.bp b/media/tests/benchmark/src/native/encoder/Android.bp
index 239f378..8de7823 100644
--- a/media/tests/benchmark/src/native/encoder/Android.bp
+++ b/media/tests/benchmark/src/native/encoder/Android.bp
@@ -23,11 +23,31 @@
srcs: ["Encoder.cpp"],
- static_libs: ["libmediabenchmark_extractor",
- "libmediabenchmark_decoder",
+ static_libs: [
+ "libmediabenchmark_extractor",
+ "libmediabenchmark_decoder",
],
export_include_dirs: ["."],
- ldflags: ["-Wl,-Bsymbolic"]
+ ldflags: ["-Wl,-Bsymbolic"],
+}
+
+cc_library_static {
+ name: "libmediabenchmark_codec2_encoder",
+ defaults: [
+ "libmediabenchmark_codec2_common-defaults",
+ ],
+
+ srcs: ["C2Encoder.cpp"],
+
+ static_libs: [
+ "libmediabenchmark_codec2_common",
+ "libmediabenchmark_codec2_extractor",
+ "libmediabenchmark_codec2_decoder",
+ ],
+
+ export_include_dirs: ["."],
+
+ ldflags: ["-Wl,-Bsymbolic"],
}
diff --git a/media/tests/benchmark/src/native/encoder/C2Encoder.cpp b/media/tests/benchmark/src/native/encoder/C2Encoder.cpp
new file mode 100644
index 0000000..33429ef
--- /dev/null
+++ b/media/tests/benchmark/src/native/encoder/C2Encoder.cpp
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2019 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 "C2Encoder"
+
+#include "C2Encoder.h"
+
+int32_t C2Encoder::createCodec2Component(string compName, AMediaFormat *format) {
+ ALOGV("In %s", __func__);
+ mListener.reset(new CodecListener(
+ [this](std::list<std::unique_ptr<C2Work>> &workItems) { handleWorkDone(workItems); }));
+ if (!mListener) return -1;
+
+ const char *mime = nullptr;
+ AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime);
+ if (!mime) {
+ ALOGE("Error in AMediaFormat_getString");
+ return -1;
+ }
+ // Configure the plugin with Input properties
+ std::vector<C2Param *> configParam;
+ if (!strncmp(mime, "audio/", 6)) {
+ mIsAudioEncoder = true;
+ int32_t numChannels;
+ if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &mSampleRate)) {
+ ALOGE("AMEDIAFORMAT_KEY_SAMPLE_RATE not set");
+ return -1;
+ }
+ if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &numChannels)) {
+ ALOGE("AMEDIAFORMAT_KEY_CHANNEL_COUNT not set");
+ return -1;
+ }
+ C2StreamSampleRateInfo::input sampleRateInfo(0u, mSampleRate);
+ C2StreamChannelCountInfo::input channelCountInfo(0u, numChannels);
+ configParam.push_back(&sampleRateInfo);
+ configParam.push_back(&channelCountInfo);
+ } else {
+ mIsAudioEncoder = false;
+ if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &mWidth)) {
+ ALOGE("AMEDIAFORMAT_KEY_WIDTH not set");
+ return -1;
+ }
+ if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &mHeight)) {
+ ALOGE("AMEDIAFORMAT_KEY_HEIGHT not set");
+ return -1;
+ }
+ C2StreamPictureSizeInfo::input inputSize(0u, mWidth, mHeight);
+ configParam.push_back(&inputSize);
+
+ if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_FRAME_RATE, &mFrameRate) ||
+ (mFrameRate <= 0)) {
+ mFrameRate = KDefaultFrameRate;
+ }
+ }
+
+ int64_t sTime = mStats->getCurTime();
+ mComponent = mClient->CreateComponentByName(compName.c_str(), mListener, &mClient);
+ if (mComponent == nullptr) {
+ ALOGE("Create component failed for %s", compName.c_str());
+ return -1;
+ }
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ int32_t status = mComponent->config(configParam, C2_DONT_BLOCK, &failures);
+ if (failures.size() != 0) {
+ ALOGE("Invalid Configuration");
+ return -1;
+ }
+
+ status |= mComponent->start();
+ int64_t eTime = mStats->getCurTime();
+ int64_t timeTaken = mStats->getTimeDiff(sTime, eTime);
+ mStats->setInitTime(timeTaken);
+ return status;
+}
+
+// In encoder components, fetch the size of input buffer allocated
+int32_t C2Encoder::getInputMaxBufSize() {
+ int32_t bitStreamInfo[1] = {0};
+ std::vector<std::unique_ptr<C2Param>> inParams;
+ c2_status_t status = mComponent->query({}, {C2StreamMaxBufferSizeInfo::input::PARAM_TYPE},
+ C2_DONT_BLOCK, &inParams);
+ if (status != C2_OK && inParams.size() == 0) {
+ ALOGE("Query MaxBufferSizeInfo failed => %d", status);
+ return status;
+ } else {
+ size_t offset = sizeof(C2Param);
+ for (size_t i = 0; i < inParams.size(); ++i) {
+ C2Param *param = inParams[i].get();
+ bitStreamInfo[i] = *(int32_t *)((uint8_t *)param + offset);
+ }
+ }
+ mInputMaxBufSize = bitStreamInfo[0];
+ if (mInputMaxBufSize < 0) {
+ ALOGE("Invalid mInputMaxBufSize %d\n", mInputMaxBufSize);
+ return -1;
+ }
+ return status;
+}
+
+int32_t C2Encoder::encodeFrames(ifstream &eleStream, size_t inputBufferSize) {
+ ALOGV("In %s", __func__);
+ int32_t frameSize = 0;
+ if (!mIsAudioEncoder) {
+ frameSize = mWidth * mHeight * 3 / 2;
+ } else {
+ frameSize = DEFAULT_AUDIO_FRAME_SIZE;
+ if (getInputMaxBufSize() != 0) return -1;
+ if (frameSize > mInputMaxBufSize) {
+ frameSize = mInputMaxBufSize;
+ }
+ }
+ int32_t numFrames = (inputBufferSize + frameSize - 1) / frameSize;
+ // Temporary buffer to read data from the input file
+ char *data = (char *)malloc(frameSize);
+ if (!data) {
+ ALOGE("Insufficient memory to read from input file");
+ return -1;
+ }
+
+ typedef std::unique_lock<std::mutex> ULock;
+ uint64_t presentationTimeUs = 0;
+ size_t offset = 0;
+ c2_status_t status = C2_OK;
+
+ mStats->setStartTime();
+ while (numFrames > 0) {
+ std::unique_ptr<C2Work> work;
+ // Prepare C2Work
+ {
+ ULock l(mQueueLock);
+ if (mWorkQueue.empty()) mQueueCondition.wait_for(l, MAX_RETRY * TIME_OUT);
+ if (!mWorkQueue.empty()) {
+ mStats->addInputTime();
+ work.swap(mWorkQueue.front());
+ mWorkQueue.pop_front();
+ } else {
+ cout << "Wait for generating C2Work exceeded timeout" << endl;
+ return -1;
+ }
+ }
+
+ if (mIsAudioEncoder) {
+ presentationTimeUs = mNumInputFrame * frameSize * (1000000 / mSampleRate);
+ } else {
+ presentationTimeUs = mNumInputFrame * (1000000 / mFrameRate);
+ }
+ uint32_t flags = 0;
+ if (numFrames == 1) flags |= C2FrameData::FLAG_END_OF_STREAM;
+
+ work->input.flags = (C2FrameData::flags_t)flags;
+ work->input.ordinal.timestamp = presentationTimeUs;
+ work->input.ordinal.frameIndex = mNumInputFrame;
+ work->input.buffers.clear();
+
+ if (inputBufferSize - offset < frameSize) {
+ frameSize = inputBufferSize - offset;
+ }
+ eleStream.read(data, frameSize);
+ if (eleStream.gcount() != frameSize) {
+ ALOGE("read() from file failed. Incorrect bytes read");
+ return -1;
+ }
+ offset += frameSize;
+
+ if (frameSize) {
+ if (mIsAudioEncoder) {
+ std::shared_ptr<C2LinearBlock> block;
+ status = mLinearPool->fetchLinearBlock(
+ frameSize, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block);
+ if (status != C2_OK || !block) {
+ cout << "fetchLinearBlock failed : " << status << endl;
+ return status;
+ }
+ C2WriteView view = block->map().get();
+ if (view.error() != C2_OK) {
+ cout << "C2LinearBlock::map() failed : " << view.error() << endl;
+ return view.error();
+ }
+
+ memcpy(view.base(), data, frameSize);
+ work->input.buffers.emplace_back(new LinearBuffer(block));
+ } else {
+ std::shared_ptr<C2GraphicBlock> block;
+ status = mGraphicPool->fetchGraphicBlock(
+ mWidth, mHeight, HAL_PIXEL_FORMAT_YV12,
+ {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block);
+ if (status != C2_OK || !block) {
+ cout << "fetchGraphicBlock failed : " << status << endl;
+ return status;
+ }
+ C2GraphicView view = block->map().get();
+ if (view.error() != C2_OK) {
+ cout << "C2GraphicBlock::map() failed : " << view.error() << endl;
+ return view.error();
+ }
+
+ uint8_t *pY = view.data()[C2PlanarLayout::PLANE_Y];
+ uint8_t *pU = view.data()[C2PlanarLayout::PLANE_U];
+ uint8_t *pV = view.data()[C2PlanarLayout::PLANE_V];
+ memcpy(pY, data, mWidth * mHeight);
+ memcpy(pU, data + mWidth * mHeight, (mWidth * mHeight >> 2));
+ memcpy(pV, data + (mWidth * mHeight * 5 >> 2), mWidth * mHeight >> 2);
+ work->input.buffers.emplace_back(new GraphicBuffer(block));
+ }
+ mStats->addFrameSize(frameSize);
+ }
+
+ work->worklets.clear();
+ work->worklets.emplace_back(new C2Worklet);
+
+ std::list<std::unique_ptr<C2Work>> items;
+ items.push_back(std::move(work));
+ // queue() invokes process() function of C2 Plugin.
+ status = mComponent->queue(&items);
+ if (status != C2_OK) {
+ ALOGE("queue failed");
+ return status;
+ }
+ ALOGV("Frame #%d size = %d queued", mNumInputFrame, frameSize);
+ numFrames--;
+ mNumInputFrame++;
+ }
+ free(data);
+ return status;
+}
+
+void C2Encoder::deInitCodec() {
+ ALOGV("In %s", __func__);
+ if (!mComponent) return;
+
+ int64_t sTime = mStats->getCurTime();
+ mComponent->stop();
+ mComponent->release();
+ mComponent = nullptr;
+ int64_t eTime = mStats->getCurTime();
+ int64_t timeTaken = mStats->getTimeDiff(sTime, eTime);
+ mStats->setDeInitTime(timeTaken);
+}
+
+void C2Encoder::dumpStatistics(string inputReference, int64_t durationUs) {
+ string operation = "c2encode";
+ mStats->dumpStatistics(operation, inputReference, durationUs);
+}
+
+void C2Encoder::resetEncoder() {
+ mIsAudioEncoder = false;
+ mNumInputFrame = 0;
+ mEos = false;
+ if (mStats) mStats->reset();
+}
diff --git a/media/tests/benchmark/src/native/encoder/C2Encoder.h b/media/tests/benchmark/src/native/encoder/C2Encoder.h
new file mode 100644
index 0000000..a4ca097
--- /dev/null
+++ b/media/tests/benchmark/src/native/encoder/C2Encoder.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __C2_ENCODER_H__
+#define __C2_ENCODER_H__
+
+#include <stdio.h>
+#include <algorithm>
+#include <fstream>
+
+#include "BenchmarkC2Common.h"
+
+#define DEFAULT_AUDIO_FRAME_SIZE 4096
+
+constexpr int32_t KDefaultFrameRate = 25;
+
+class C2Encoder : public BenchmarkC2Common {
+ public:
+ C2Encoder()
+ : mIsAudioEncoder(false),
+ mWidth(0),
+ mHeight(0),
+ mNumInputFrame(0),
+ mComponent(nullptr) {}
+
+ int32_t createCodec2Component(string codecName, AMediaFormat *format);
+
+ int32_t encodeFrames(ifstream &eleStream, size_t inputBufferSize);
+
+ int32_t getInputMaxBufSize();
+
+ void deInitCodec();
+
+ void dumpStatistics(string inputReference, int64_t durationUs);
+
+ void resetEncoder();
+
+ private:
+ bool mIsAudioEncoder;
+
+ int32_t mWidth;
+ int32_t mHeight;
+ int32_t mFrameRate;
+ int32_t mSampleRate;
+
+ int32_t mNumInputFrame;
+ int32_t mInputMaxBufSize;
+
+ std::shared_ptr<android::Codec2Client::Listener> mListener;
+ std::shared_ptr<android::Codec2Client::Component> mComponent;
+};
+
+#endif // __C2_ENCODER_H__
diff --git a/media/tests/benchmark/src/native/encoder/Encoder.cpp b/media/tests/benchmark/src/native/encoder/Encoder.cpp
index a5605de..2db612c 100644
--- a/media/tests/benchmark/src/native/encoder/Encoder.cpp
+++ b/media/tests/benchmark/src/native/encoder/Encoder.cpp
@@ -154,11 +154,12 @@
}
void Encoder::deInitCodec() {
- int64_t sTime = mStats->getCurTime();
if (mFormat) {
AMediaFormat_delete(mFormat);
mFormat = nullptr;
}
+ if (!mCodec) return;
+ int64_t sTime = mStats->getCurTime();
AMediaCodec_stop(mCodec);
AMediaCodec_delete(mCodec);
int64_t eTime = mStats->getCurTime();
@@ -174,9 +175,10 @@
memset(&mParams, 0, sizeof mParams);
}
-void Encoder::dumpStatistics(string inputReference, int64_t durationUs) {
+void Encoder::dumpStatistics(string inputReference, int64_t durationUs, string componentName,
+ string mode, string statsFile) {
string operation = "encode";
- mStats->dumpStatistics(operation, inputReference, durationUs);
+ mStats->dumpStatistics(operation, inputReference, durationUs, componentName, mode, statsFile);
}
int32_t Encoder::encode(string &codecName, ifstream &eleStream, size_t eleSize,
@@ -206,7 +208,7 @@
AMediaFormat_setInt32(mFormat, AMEDIAFORMAT_KEY_BIT_RATE, mParams.bitrate);
}
const char *s = AMediaFormat_toString(mFormat);
- ALOGV("Input format: %s\n", s);
+ ALOGI("Input format: %s\n", s);
int64_t sTime = mStats->getCurTime();
mCodec = createMediaCodec(mFormat, mMime, codecName, true /*isEncoder*/);
diff --git a/media/tests/benchmark/src/native/encoder/Encoder.h b/media/tests/benchmark/src/native/encoder/Encoder.h
index 6059c4a..3d12600 100644
--- a/media/tests/benchmark/src/native/encoder/Encoder.h
+++ b/media/tests/benchmark/src/native/encoder/Encoder.h
@@ -75,7 +75,8 @@
int32_t encode(std::string &codecName, std::ifstream &eleStream, size_t eleSize, bool asyncMode,
encParameter encParams, char *mime);
- void dumpStatistics(string inputReference, int64_t durationUs);
+ void dumpStatistics(string inputReference, int64_t durationUs, string codecName = "",
+ string mode = "", string statsFile = "");
private:
AMediaCodec *mCodec;
diff --git a/media/tests/benchmark/src/native/extractor/Android.bp b/media/tests/benchmark/src/native/extractor/Android.bp
index dfd0d49..7ed9476 100644
--- a/media/tests/benchmark/src/native/extractor/Android.bp
+++ b/media/tests/benchmark/src/native/extractor/Android.bp
@@ -27,3 +27,20 @@
ldflags: ["-Wl,-Bsymbolic"]
}
+
+cc_library_static {
+ name: "libmediabenchmark_codec2_extractor",
+ defaults: [
+ "libmediabenchmark_codec2_common-defaults",
+ ],
+
+ srcs: ["Extractor.cpp"],
+
+ static_libs: [
+ "libmediabenchmark_codec2_common",
+ ],
+
+ export_include_dirs: ["."],
+
+ ldflags: ["-Wl,-Bsymbolic"]
+}
diff --git a/media/tests/benchmark/src/native/extractor/Extractor.cpp b/media/tests/benchmark/src/native/extractor/Extractor.cpp
index b4cad0b..f0bb3b9 100644
--- a/media/tests/benchmark/src/native/extractor/Extractor.cpp
+++ b/media/tests/benchmark/src/native/extractor/Extractor.cpp
@@ -111,9 +111,9 @@
return AMEDIA_OK;
}
-void Extractor::dumpStatistics(string inputReference) {
+void Extractor::dumpStatistics(string inputReference, string componentName, string statsFile) {
string operation = "extract";
- mStats->dumpStatistics(operation, inputReference, mDurationUs);
+ mStats->dumpStatistics(operation, inputReference, mDurationUs, componentName, "", statsFile);
}
void Extractor::deInitExtractor() {
diff --git a/media/tests/benchmark/src/native/extractor/Extractor.h b/media/tests/benchmark/src/native/extractor/Extractor.h
index 4c39a72..1694fc7 100644
--- a/media/tests/benchmark/src/native/extractor/Extractor.h
+++ b/media/tests/benchmark/src/native/extractor/Extractor.h
@@ -45,7 +45,7 @@
int32_t extract(int32_t trackId);
- void dumpStatistics(std::string inputReference);
+ void dumpStatistics(string inputReference, string componentName = "", string statsFile = "");
void deInitExtractor();
diff --git a/media/tests/benchmark/src/native/muxer/Muxer.cpp b/media/tests/benchmark/src/native/muxer/Muxer.cpp
index b297a66..3e150ca 100644
--- a/media/tests/benchmark/src/native/muxer/Muxer.cpp
+++ b/media/tests/benchmark/src/native/muxer/Muxer.cpp
@@ -29,7 +29,7 @@
int64_t sTime = mStats->getCurTime();
mMuxer = AMediaMuxer_new(fd, (OutputFormat)outputFormat);
if (!mMuxer) {
- cout << "[ WARN ] Test Skipped. Unable to create muxer \n";
+ ALOGV("Unable to create muxer");
return AMEDIA_ERROR_INVALID_OBJECT;
}
/*
@@ -38,7 +38,7 @@
*/
ssize_t index = AMediaMuxer_addTrack(mMuxer, mFormat);
if (index < 0) {
- cout << "[ WARN ] Test Skipped. Format not supported \n";
+ ALOGV("Format not supported");
return index;
}
AMediaMuxer_start(mMuxer);
@@ -49,12 +49,12 @@
}
void Muxer::deInitMuxer() {
- int64_t sTime = mStats->getCurTime();
if (mFormat) {
AMediaFormat_delete(mFormat);
mFormat = nullptr;
}
if (!mMuxer) return;
+ int64_t sTime = mStats->getCurTime();
AMediaMuxer_stop(mMuxer);
AMediaMuxer_delete(mMuxer);
int64_t eTime = mStats->getCurTime();
@@ -66,9 +66,10 @@
if (mStats) mStats->reset();
}
-void Muxer::dumpStatistics(string inputReference) {
+void Muxer::dumpStatistics(string inputReference, string componentName, string statsFile) {
string operation = "mux";
- mStats->dumpStatistics(operation, inputReference, mExtractor->getClipDuration());
+ mStats->dumpStatistics(operation, inputReference, mExtractor->getClipDuration(), componentName,
+ "", statsFile);
}
int32_t Muxer::mux(uint8_t *inputBuffer, vector<AMediaCodecBufferInfo> &frameInfos) {
diff --git a/media/tests/benchmark/src/native/muxer/Muxer.h b/media/tests/benchmark/src/native/muxer/Muxer.h
index eee3146..860fdaf 100644
--- a/media/tests/benchmark/src/native/muxer/Muxer.h
+++ b/media/tests/benchmark/src/native/muxer/Muxer.h
@@ -51,7 +51,7 @@
/* Process the frames and give Muxed output */
int32_t mux(uint8_t *inputBuffer, vector<AMediaCodecBufferInfo> &frameSizes);
- void dumpStatistics(string inputReference);
+ void dumpStatistics(string inputReference, string codecName = "", string statsFile = "");
private:
AMediaFormat *mFormat;
diff --git a/media/tests/benchmark/tests/Android.bp b/media/tests/benchmark/tests/Android.bp
index 128d055..f46fa4a 100644
--- a/media/tests/benchmark/tests/Android.bp
+++ b/media/tests/benchmark/tests/Android.bp
@@ -87,8 +87,25 @@
srcs: ["C2DecoderTest.cpp"],
static_libs: [
- "libmediabenchmark_extractor",
+ "libmediabenchmark_codec2_extractor",
"libmediabenchmark_codec2_common",
"libmediabenchmark_codec2_decoder",
],
}
+
+cc_test {
+ name: "C2EncoderTest",
+ gtest: true,
+ defaults: [
+ "libmediabenchmark_codec2_common-defaults",
+ ],
+
+ srcs: ["C2EncoderTest.cpp"],
+
+ static_libs: [
+ "libmediabenchmark_codec2_extractor",
+ "libmediabenchmark_codec2_decoder",
+ "libmediabenchmark_codec2_common",
+ "libmediabenchmark_codec2_encoder",
+ ],
+}
diff --git a/media/tests/benchmark/tests/C2DecoderTest.cpp b/media/tests/benchmark/tests/C2DecoderTest.cpp
index 3531d8a..ecd9759 100644
--- a/media/tests/benchmark/tests/C2DecoderTest.cpp
+++ b/media/tests/benchmark/tests/C2DecoderTest.cpp
@@ -29,82 +29,64 @@
class C2DecoderTest : public ::testing::TestWithParam<pair<string, string>> {
public:
- C2DecoderTest() : mDecoder(nullptr), disableTest(false) { setupC2DecoderTest(); }
+ C2DecoderTest() : mDecoder(nullptr) {}
+
+ ~C2DecoderTest() {
+ if (!mCodecList.empty()) {
+ mCodecList.clear();
+ }
+ if (mDecoder) {
+ delete mDecoder;
+ mDecoder = nullptr;
+ }
+ }
+
+ virtual void SetUp() override { setupC2DecoderTest(); }
void setupC2DecoderTest();
vector<string> mCodecList;
C2Decoder *mDecoder;
- bool disableTest;
};
void C2DecoderTest::setupC2DecoderTest() {
mDecoder = new C2Decoder();
- if (!mDecoder) {
- cout << "[ WARN ] Test Skipped. C2Decoder creation failed\n";
- disableTest = true;
- return;
- }
+ ASSERT_NE(mDecoder, nullptr) << "C2Decoder creation failed";
+
int32_t status = mDecoder->setupCodec2();
- if (status != 0) {
- cout << "[ WARN ] Test Skipped. Codec2 setup failed \n";
- disableTest = true;
- return;
- }
+ ASSERT_EQ(status, 0) << "Codec2 setup failed";
+
mCodecList = mDecoder->getSupportedComponentList(false /* isEncoder*/);
- if (!mCodecList.size()) {
- cout << "[ WARN ] Test Skipped. Codec2 client didn't recognise any component \n";
- disableTest = true;
- return;
- }
+ ASSERT_GT(mCodecList.size(), 0) << "Codec2 client didn't recognise any component";
}
TEST_P(C2DecoderTest, Codec2Decode) {
- if (disableTest) return;
-
ALOGV("Decode the samples given by extractor using codec2");
string inputFile = gEnv->getRes() + GetParam().first;
FILE *inputFp = fopen(inputFile.c_str(), "rb");
- if (!inputFp) {
- cout << "[ WARN ] Test Skipped. Unable to open input file" << inputFile
- << " for reading \n";
- return;
- }
+ ASSERT_NE(inputFp, nullptr) << "Unable to open " << inputFile << " file for reading";
Extractor *extractor = new Extractor();
- if (!extractor) {
- cout << "[ WARN ] Test Skipped. Extractor creation failed \n";
- return;
- }
+ ASSERT_NE(extractor, nullptr) << "Extractor creation failed";
// Read file properties
- fseek(inputFp, 0, SEEK_END);
- size_t fileSize = ftell(inputFp);
- fseek(inputFp, 0, SEEK_SET);
+ struct stat buf;
+ stat(inputFile.c_str(), &buf);
+ size_t fileSize = buf.st_size;
int32_t fd = fileno(inputFp);
- if (fileSize > kMaxBufferSize) {
- cout << "[ WARN ] Test Skipped. Input file size is greater than the threshold memory "
- "dedicated to the test \n";
- }
+ ASSERT_LE(fileSize, kMaxBufferSize)
+ << "Input file size is greater than the threshold memory dedicated to the test";
int32_t trackCount = extractor->initExtractor(fd, fileSize);
- if (trackCount <= 0) {
- cout << "[ WARN ] Test Skipped. initExtractor failed\n";
- return;
- }
+ ASSERT_GT(trackCount, 0) << "initExtractor failed";
+
for (int32_t curTrack = 0; curTrack < trackCount; curTrack++) {
int32_t status = extractor->setupTrackFormat(curTrack);
- if (status != 0) {
- cout << "[ WARN ] Test Skipped. Track Format invalid \n";
- return;
- }
+ ASSERT_EQ(status, 0) << "Track Format invalid";
uint8_t *inputBuffer = (uint8_t *)malloc(fileSize);
- if (!inputBuffer) {
- cout << "[ WARN ] Test Skipped. Insufficient memory \n";
- return;
- }
+ ASSERT_NE(inputBuffer, nullptr) << "Insufficient memory";
vector<AMediaCodecBufferInfo> frameInfo;
AMediaCodecBufferInfo info;
@@ -116,11 +98,8 @@
void *csdBuffer = extractor->getCSDSample(info, idx);
if (!csdBuffer || !info.size) break;
// copy the meta data and buffer to be passed to decoder
- if (inputBufferOffset + info.size > fileSize) {
- cout << "[ WARN ] Test Skipped. Memory allocated not sufficient\n";
- free(inputBuffer);
- return;
- }
+ ASSERT_LE(inputBufferOffset + info.size, fileSize) << "Memory allocated not sufficient";
+
memcpy(inputBuffer + inputBufferOffset, csdBuffer, info.size);
frameInfo.push_back(info);
inputBufferOffset += info.size;
@@ -132,11 +111,8 @@
status = extractor->getFrameSample(info);
if (status || !info.size) break;
// copy the meta data and buffer to be passed to decoder
- if (inputBufferOffset + info.size > fileSize) {
- cout << "[ WARN ] Test Skipped. Memory allocated not sufficient\n";
- free(inputBuffer);
- return;
- }
+ ASSERT_LE(inputBufferOffset + info.size, fileSize) << "Memory allocated not sufficient";
+
memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(), info.size);
frameInfo.push_back(info);
inputBufferOffset += info.size;
@@ -148,21 +124,18 @@
if (codecName.find(GetParam().second) != string::npos &&
codecName.find("secure") == string::npos) {
status = mDecoder->createCodec2Component(codecName, format);
- if (status != 0) {
- cout << "[ WARN ] Test Skipped. Create component failed for " << codecName
- << "\n";
- continue;
- }
+ ASSERT_EQ(status, 0) << "Create component failed for " << codecName;
// Send the inputs to C2 Decoder and wait till all buffers are returned.
- mDecoder->decodeFrames(inputBuffer, frameInfo);
+ status = mDecoder->decodeFrames(inputBuffer, frameInfo);
+ ASSERT_EQ(status, 0) << "Decoder failed for " << codecName;
+
mDecoder->waitOnInputConsumption();
- if (!mDecoder->mEos) {
- cout << "[ WARN ] Test Failed. Didn't receive EOS \n";
- }
+ ASSERT_TRUE(mDecoder->mEos) << "Test Failed. Didn't receive EOS \n";
+
mDecoder->deInitCodec();
int64_t durationUs = extractor->getClipDuration();
- cout << "codec: " << codecName << endl;
+ ALOGV("codec : %s", codecName.c_str());
mDecoder->dumpStatistics(GetParam().first, durationUs);
mDecoder->resetDecoder();
}
@@ -172,6 +145,7 @@
extractor->deInitExtractor();
delete extractor;
delete mDecoder;
+ mDecoder = nullptr;
}
}
@@ -179,26 +153,24 @@
// Add wav files
INSTANTIATE_TEST_SUITE_P(
AudioDecoderTest, C2DecoderTest,
- ::testing::Values(
- make_pair("bbb_44100hz_2ch_128kbps_aac_30sec.mp4", "aac"),
- make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec.mp3", "mp3"),
- make_pair("bbb_8000hz_1ch_8kbps_amrnb_30sec.3gp", "amrnb"),
- make_pair("bbb_16000hz_1ch_9kbps_amrwb_30sec.3gp", "amrnb"),
- make_pair("bbb_44100hz_2ch_80kbps_vorbis_30sec.mp4", "vorbis"),
- make_pair("bbb_44100hz_2ch_600kbps_flac_30sec.mp4", "flac"),
- make_pair("bbb_48000hz_2ch_100kbps_opus_30sec.webm", "opus")));
+ ::testing::Values(make_pair("bbb_44100hz_2ch_128kbps_aac_30sec.mp4", "aac"),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec.mp3", "mp3"),
+ make_pair("bbb_8000hz_1ch_8kbps_amrnb_30sec.3gp", "amrnb"),
+ make_pair("bbb_16000hz_1ch_9kbps_amrwb_30sec.3gp", "amrnb"),
+ make_pair("bbb_44100hz_2ch_80kbps_vorbis_30sec.mp4", "vorbis"),
+ make_pair("bbb_44100hz_2ch_600kbps_flac_30sec.mp4", "flac"),
+ make_pair("bbb_48000hz_2ch_100kbps_opus_30sec.webm", "opus")));
INSTANTIATE_TEST_SUITE_P(
VideoDecoderTest, C2DecoderTest,
- ::testing::Values(
- make_pair("crowd_1920x1080_25fps_4000kbps_vp9.webm", "vp9"),
- make_pair("crowd_1920x1080_25fps_4000kbps_vp8.webm", "vp8"),
- make_pair("crowd_1920x1080_25fps_4000kbps_av1.webm", "av1"),
- make_pair("crowd_1920x1080_25fps_7300kbps_mpeg2.mp4", "mpeg2"),
- make_pair("crowd_1920x1080_25fps_6000kbps_mpeg4.mp4", "mpeg4"),
- make_pair("crowd_352x288_25fps_6000kbps_h263.3gp", "h263"),
- make_pair("crowd_1920x1080_25fps_6700kbps_h264.ts", "avc"),
- make_pair("crowd_1920x1080_25fps_4000kbps_h265.mkv", "hevc")));
+ ::testing::Values(make_pair("crowd_1920x1080_25fps_4000kbps_vp9.webm", "vp9"),
+ make_pair("crowd_1920x1080_25fps_4000kbps_vp8.webm", "vp8"),
+ make_pair("crowd_1920x1080_25fps_4000kbps_av1.webm", "av1"),
+ make_pair("crowd_1920x1080_25fps_7300kbps_mpeg2.mp4", "mpeg2"),
+ make_pair("crowd_1920x1080_25fps_6000kbps_mpeg4.mp4", "mpeg4"),
+ make_pair("crowd_352x288_25fps_6000kbps_h263.3gp", "h263"),
+ make_pair("crowd_1920x1080_25fps_6700kbps_h264.ts", "avc"),
+ make_pair("crowd_1920x1080_25fps_4000kbps_h265.mkv", "hevc")));
int main(int argc, char **argv) {
gEnv = new BenchmarkTestEnvironment();
diff --git a/media/tests/benchmark/tests/C2EncoderTest.cpp b/media/tests/benchmark/tests/C2EncoderTest.cpp
new file mode 100644
index 0000000..98eb17a
--- /dev/null
+++ b/media/tests/benchmark/tests/C2EncoderTest.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2019 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 "C2EncoderTest"
+
+#include <fstream>
+#include <iostream>
+#include <limits>
+
+#include "BenchmarkTestEnvironment.h"
+#include "C2Encoder.h"
+#include "Decoder.h"
+
+static BenchmarkTestEnvironment *gEnv = nullptr;
+
+class C2EncoderTest : public ::testing::TestWithParam<pair<string, string>> {
+ public:
+ C2EncoderTest() : mEncoder(nullptr) {}
+
+ ~C2EncoderTest() {
+ if (!mCodecList.empty()) {
+ mCodecList.clear();
+ }
+ if (mEncoder) {
+ delete mEncoder;
+ mEncoder = nullptr;
+ }
+ }
+
+ virtual void SetUp() override { setupC2EncoderTest(); }
+
+ void setupC2EncoderTest();
+
+ vector<string> mCodecList;
+ C2Encoder *mEncoder;
+};
+
+void C2EncoderTest::setupC2EncoderTest() {
+ mEncoder = new C2Encoder();
+ ASSERT_NE(mEncoder, nullptr) << "C2Encoder creation failed";
+
+ int32_t status = mEncoder->setupCodec2();
+ ASSERT_EQ(status, 0) << "Codec2 setup failed";
+
+ mCodecList = mEncoder->getSupportedComponentList(true /* isEncoder*/);
+ ASSERT_GT(mCodecList.size(), 0) << "Codec2 client didn't recognise any component";
+}
+
+TEST_P(C2EncoderTest, Codec2Encode) {
+ ALOGV("Encodes the input using codec2 framework");
+ string inputFile = gEnv->getRes() + GetParam().first;
+ FILE *inputFp = fopen(inputFile.c_str(), "rb");
+ ASSERT_NE(inputFp, nullptr) << "Unable to open input file for reading";
+
+ Decoder *decoder = new Decoder();
+ ASSERT_NE(decoder, nullptr) << "Decoder creation failed";
+
+ Extractor *extractor = decoder->getExtractor();
+ ASSERT_NE(extractor, nullptr) << "Extractor creation failed";
+
+ // Read file properties
+ struct stat buf;
+ stat(inputFile.c_str(), &buf);
+ size_t fileSize = buf.st_size;
+ int32_t fd = fileno(inputFp);
+
+ ASSERT_LE(fileSize, kMaxBufferSize)
+ << "Input file size is greater than the threshold memory dedicated to the test";
+
+ int32_t trackCount = extractor->initExtractor(fd, fileSize);
+ ASSERT_GT(trackCount, 0) << "initExtractor failed";
+
+ for (int curTrack = 0; curTrack < trackCount; curTrack++) {
+ int32_t status = extractor->setupTrackFormat(curTrack);
+ ASSERT_EQ(status, 0) << "Track Format invalid";
+
+ uint8_t *inputBuffer = (uint8_t *)malloc(fileSize);
+ ASSERT_NE(inputBuffer, nullptr) << "Insufficient memory";
+
+ vector<AMediaCodecBufferInfo> frameInfo;
+ AMediaCodecBufferInfo info;
+ uint32_t inputBufferOffset = 0;
+
+ // Get frame data
+ while (1) {
+ status = extractor->getFrameSample(info);
+ if (status || !info.size) break;
+ // copy the meta data and buffer to be passed to decoder
+ ASSERT_LE(inputBufferOffset + info.size, fileSize) << "Memory allocated not sufficient";
+
+ memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(), info.size);
+ frameInfo.push_back(info);
+ inputBufferOffset += info.size;
+ }
+
+ string decName = "";
+ string outputFileName = "decode.out";
+ FILE *outFp = fopen(outputFileName.c_str(), "wb");
+ ASSERT_NE(outFp, nullptr) << "Unable to open output file" << outputFileName
+ << " for dumping decoder's output";
+
+ decoder->setupDecoder();
+ status = decoder->decode(inputBuffer, frameInfo, decName, false /*asyncMode */, outFp);
+ ASSERT_EQ(status, AMEDIA_OK) << "Decode returned error : " << status;
+
+ // Encode the given input stream for all C2 codecs supported by device
+ AMediaFormat *format = extractor->getFormat();
+ ifstream eleStream;
+ eleStream.open(outputFileName.c_str(), ifstream::binary | ifstream::ate);
+ ASSERT_EQ(eleStream.is_open(), true) << outputFileName.c_str() << " - file not found";
+ size_t eleSize = eleStream.tellg();
+
+ for (string codecName : mCodecList) {
+ if (codecName.find(GetParam().second) != string::npos) {
+ status = mEncoder->createCodec2Component(codecName, format);
+ ASSERT_EQ(status, 0) << "Create component failed for " << codecName;
+
+ // Send the inputs to C2 Encoder and wait till all buffers are returned.
+ eleStream.seekg(0, ifstream::beg);
+ status = mEncoder->encodeFrames(eleStream, eleSize);
+ ASSERT_EQ(status, 0) << "Encoder failed for " << codecName;
+
+ mEncoder->waitOnInputConsumption();
+ ASSERT_TRUE(mEncoder->mEos) << "Test Failed. Didn't receive EOS \n";
+
+ mEncoder->deInitCodec();
+ int64_t durationUs = extractor->getClipDuration();
+ ALOGV("codec : %s", codecName.c_str());
+ mEncoder->dumpStatistics(GetParam().first, durationUs);
+ mEncoder->resetEncoder();
+ }
+ }
+
+ // Destroy the decoder for the given input
+ decoder->deInitCodec();
+ decoder->resetDecoder();
+ free(inputBuffer);
+ }
+ fclose(inputFp);
+ extractor->deInitExtractor();
+ delete decoder;
+ delete mEncoder;
+ mEncoder = nullptr;
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AudioEncoderTest, C2EncoderTest,
+ ::testing::Values(make_pair("bbb_44100hz_2ch_128kbps_aac_30sec.mp4", "aac"),
+ make_pair("bbb_8000hz_1ch_8kbps_amrnb_30sec.3gp", "amrnb"),
+ make_pair("bbb_16000hz_1ch_9kbps_amrwb_30sec.3gp", "amrwb"),
+ make_pair("bbb_44100hz_2ch_600kbps_flac_30sec.mp4", "flac"),
+ make_pair("bbb_48000hz_2ch_100kbps_opus_30sec.webm", "opus")));
+
+INSTANTIATE_TEST_SUITE_P(
+ VideoEncoderTest, C2EncoderTest,
+ ::testing::Values(make_pair("crowd_1920x1080_25fps_4000kbps_vp9.webm", "vp9"),
+ make_pair("crowd_1920x1080_25fps_4000kbps_vp8.webm", "vp8"),
+ make_pair("crowd_176x144_25fps_6000kbps_mpeg4.mp4", "mpeg4"),
+ make_pair("crowd_176x144_25fps_6000kbps_h263.3gp", "h263"),
+ make_pair("crowd_1920x1080_25fps_6700kbps_h264.ts", "avc"),
+ make_pair("crowd_1920x1080_25fps_4000kbps_h265.mkv", "hevc")));
+
+int main(int argc, char **argv) {
+ gEnv = new BenchmarkTestEnvironment();
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = gEnv->initFromOptions(argc, argv);
+ if (status == 0) {
+ status = RUN_ALL_TESTS();
+ ALOGV("C2 Encoder Test result = %d\n", status);
+ }
+ return status;
+}
diff --git a/media/tests/benchmark/tests/DecoderTest.cpp b/media/tests/benchmark/tests/DecoderTest.cpp
index fa37435..5c6aa5b 100644
--- a/media/tests/benchmark/tests/DecoderTest.cpp
+++ b/media/tests/benchmark/tests/DecoderTest.cpp
@@ -21,8 +21,8 @@
#include <iostream>
#include <limits>
-#include "Decoder.h"
#include "BenchmarkTestEnvironment.h"
+#include "Decoder.h"
static BenchmarkTestEnvironment *gEnv = nullptr;
@@ -34,41 +34,30 @@
string inputFile = gEnv->getRes() + get<0>(params);
FILE *inputFp = fopen(inputFile.c_str(), "rb");
- if (!inputFp) {
- cout << "[ WARN ] Test Skipped. Unable to open input file for reading \n";
- return;
- }
+ ASSERT_NE(inputFp, nullptr) << "Unable to open " << inputFile << " file for reading";
Decoder *decoder = new Decoder();
+ ASSERT_NE(decoder, nullptr) << "Decoder creation failed";
+
Extractor *extractor = decoder->getExtractor();
- if (!extractor) {
- cout << "[ WARN ] Test Skipped. Extractor creation failed \n";
- return;
- }
+ ASSERT_NE(extractor, nullptr) << "Extractor creation failed";
// Read file properties
- fseek(inputFp, 0, SEEK_END);
- size_t fileSize = ftell(inputFp);
- fseek(inputFp, 0, SEEK_SET);
+ struct stat buf;
+ stat(inputFile.c_str(), &buf);
+ size_t fileSize = buf.st_size;
int32_t fd = fileno(inputFp);
int32_t trackCount = extractor->initExtractor(fd, fileSize);
- if (trackCount <= 0) {
- cout << "[ WARN ] Test Skipped. initExtractor failed\n";
- return;
- }
+ ASSERT_GT(trackCount, 0) << "initExtractor failed";
+
for (int curTrack = 0; curTrack < trackCount; curTrack++) {
int32_t status = extractor->setupTrackFormat(curTrack);
- if (status != 0) {
- cout << "[ WARN ] Test Skipped. Track Format invalid \n";
- return;
- }
+ ASSERT_EQ(status, 0) << "Track Format invalid";
uint8_t *inputBuffer = (uint8_t *)malloc(kMaxBufferSize);
- if (!inputBuffer) {
- cout << "[ WARN ] Test Skipped. Insufficient memory \n";
- return;
- }
+ ASSERT_NE(inputBuffer, nullptr) << "Insufficient memory";
+
vector<AMediaCodecBufferInfo> frameInfo;
AMediaCodecBufferInfo info;
uint32_t inputBufferOffset = 0;
@@ -78,11 +67,9 @@
status = extractor->getFrameSample(info);
if (status || !info.size) break;
// copy the meta data and buffer to be passed to decoder
- if (inputBufferOffset + info.size > kMaxBufferSize) {
- cout << "[ WARN ] Test Skipped. Memory allocated not sufficient\n";
- free(inputBuffer);
- return;
- }
+ ASSERT_LE(inputBufferOffset + info.size, kMaxBufferSize)
+ << "Memory allocated not sufficient";
+
memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(), info.size);
frameInfo.push_back(info);
inputBufferOffset += info.size;
@@ -92,13 +79,10 @@
bool asyncMode = get<2>(params);
decoder->setupDecoder();
status = decoder->decode(inputBuffer, frameInfo, codecName, asyncMode);
- if (status != AMEDIA_OK) {
- cout << "[ WARN ] Test Failed. Decode returned error " << status << endl;
- free(inputBuffer);
- return;
- }
+ ASSERT_EQ(status, AMEDIA_OK) << "Decoder failed for " << codecName;
+
decoder->deInitCodec();
- cout << "codec : " << codecName << endl;
+ ALOGV("codec : %s", codecName.c_str());
string inputReference = get<0>(params);
decoder->dumpStatistics(inputReference);
free(inputBuffer);
diff --git a/media/tests/benchmark/tests/EncoderTest.cpp b/media/tests/benchmark/tests/EncoderTest.cpp
index c3963f8..dc2a2dd 100644
--- a/media/tests/benchmark/tests/EncoderTest.cpp
+++ b/media/tests/benchmark/tests/EncoderTest.cpp
@@ -20,8 +20,8 @@
#include <fstream>
#include "BenchmarkTestEnvironment.h"
-#include "Encoder.h"
#include "Decoder.h"
+#include "Encoder.h"
static BenchmarkTestEnvironment *gEnv = nullptr;
@@ -33,42 +33,33 @@
string inputFile = gEnv->getRes() + get<0>(params);
FILE *inputFp = fopen(inputFile.c_str(), "rb");
- if (!inputFp) {
- cout << "[ WARN ] Test Skipped. Unable to open input file for reading \n";
- return;
- }
+ ASSERT_NE(inputFp, nullptr) << "Unable to open " << inputFile << " file for reading";
Decoder *decoder = new Decoder();
+ ASSERT_NE(decoder, nullptr) << "Decoder creation failed";
+
Extractor *extractor = decoder->getExtractor();
- if (!extractor) {
- cout << "[ WARN ] Test Skipped. Extractor creation failed \n";
- return;
- }
+ ASSERT_NE(extractor, nullptr) << "Extractor creation failed";
+
// Read file properties
- fseek(inputFp, 0, SEEK_END);
- size_t fileSize = ftell(inputFp);
- fseek(inputFp, 0, SEEK_SET);
+ struct stat buf;
+ stat(inputFile.c_str(), &buf);
+ size_t fileSize = buf.st_size;
int32_t fd = fileno(inputFp);
int32_t trackCount = extractor->initExtractor(fd, fileSize);
- if (trackCount <= 0) {
- cout << "[ WARN ] Test Skipped. initExtractor failed\n";
- return;
- }
+ ASSERT_GT(trackCount, 0) << "initExtractor failed";
Encoder *encoder = new Encoder();
+ ASSERT_NE(encoder, nullptr) << "Decoder creation failed";
+
for (int curTrack = 0; curTrack < trackCount; curTrack++) {
int32_t status = extractor->setupTrackFormat(curTrack);
- if (status != 0) {
- cout << "[ WARN ] Test Skipped. Track Format invalid \n";
- return;
- }
+ ASSERT_EQ(status, 0) << "Track Format invalid";
uint8_t *inputBuffer = (uint8_t *)malloc(kMaxBufferSize);
- if (!inputBuffer) {
- cout << "[ WARN ] Test Skipped. Insufficient memory \n";
- return;
- }
+ ASSERT_NE(inputBuffer, nullptr) << "Insufficient memory";
+
vector<AMediaCodecBufferInfo> frameInfo;
AMediaCodecBufferInfo info;
uint32_t inputBufferOffset = 0;
@@ -78,11 +69,9 @@
status = extractor->getFrameSample(info);
if (status || !info.size) break;
// copy the meta data and buffer to be passed to decoder
- if (inputBufferOffset + info.size > kMaxBufferSize) {
- cout << "[ WARN ] Test Skipped. Memory allocated not sufficient\n";
- free(inputBuffer);
- return;
- }
+ ASSERT_LE(inputBufferOffset + info.size, kMaxBufferSize)
+ << "Memory allocated not sufficient";
+
memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(), info.size);
frameInfo.push_back(info);
inputBufferOffset += info.size;
@@ -91,16 +80,12 @@
string decName = "";
string outputFileName = "decode.out";
FILE *outFp = fopen(outputFileName.c_str(), "wb");
- if (outFp == nullptr) {
- ALOGE("Unable to open output file for writing");
- return;
- }
+ ASSERT_NE(outFp, nullptr) << "Unable to open output file" << outputFileName
+ << " for dumping decoder's output";
+
decoder->setupDecoder();
status = decoder->decode(inputBuffer, frameInfo, decName, false /*asyncMode */, outFp);
- if (status != AMEDIA_OK) {
- cout << "[ WARN ] Test Skipped. Decode returned error \n";
- return;
- }
+ ASSERT_EQ(status, AMEDIA_OK) << "Decode returned error : " << status;
ifstream eleStream;
eleStream.open(outputFileName.c_str(), ifstream::binary | ifstream::ate);
@@ -111,15 +96,13 @@
AMediaFormat *format = extractor->getFormat();
const char *mime = nullptr;
AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime);
- if (!mime) {
- ALOGE("Error in AMediaFormat_getString");
- return;
- }
+ ASSERT_NE(mime, nullptr) << "Invalid mime type";
+
// Get encoder params
encParameter encParams;
if (!strncmp(mime, "video/", 6)) {
- AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &encParams.width);
- AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &encParams.height);
+ ASSERT_TRUE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &encParams.width));
+ ASSERT_TRUE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &encParams.height));
AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_FRAME_RATE, &encParams.frameRate);
AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, &encParams.bitrate);
if (encParams.bitrate <= 0 || encParams.frameRate <= 0) {
@@ -133,8 +116,10 @@
AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_PROFILE, &encParams.profile);
AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_LEVEL, &encParams.level);
} else {
- AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &encParams.sampleRate);
- AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &encParams.numChannels);
+ ASSERT_TRUE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE,
+ &encParams.sampleRate));
+ ASSERT_TRUE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT,
+ &encParams.numChannels));
encParams.bitrate =
encParams.sampleRate * encParams.numChannels * 16 /* bitsPerSample */;
}
@@ -143,13 +128,10 @@
string codecName = get<1>(params);
bool asyncMode = get<2>(params);
status = encoder->encode(codecName, eleStream, eleSize, asyncMode, encParams, (char *)mime);
- if (status != AMEDIA_OK) {
- cout << "[ WARN ] Test Failed. Encode returned error " << status << endl;
- free(inputBuffer);
- return;
- }
+ ASSERT_EQ(status, 0) << "Encoder failed for " << codecName;
+
encoder->deInitCodec();
- cout << "codec : " << codecName << endl;
+ ALOGV("codec : %s", codecName.c_str());
string inputReference = get<0>(params);
encoder->dumpStatistics(inputReference, extractor->getClipDuration());
eleStream.close();
diff --git a/media/tests/benchmark/tests/ExtractorTest.cpp b/media/tests/benchmark/tests/ExtractorTest.cpp
index dd0d711..c2d72ff 100644
--- a/media/tests/benchmark/tests/ExtractorTest.cpp
+++ b/media/tests/benchmark/tests/ExtractorTest.cpp
@@ -19,8 +19,8 @@
#include <gtest/gtest.h>
-#include "Extractor.h"
#include "BenchmarkTestEnvironment.h"
+#include "Extractor.h"
static BenchmarkTestEnvironment *gEnv = nullptr;
@@ -28,33 +28,24 @@
TEST_P(ExtractorTest, Extract) {
Extractor *extractObj = new Extractor();
+ ASSERT_NE(extractObj, nullptr) << "Extractor creation failed";
string inputFile = gEnv->getRes() + GetParam().first;
FILE *inputFp = fopen(inputFile.c_str(), "rb");
- if (!inputFp) {
- cout << "[ WARN ] Test Skipped. Unable to open input file for reading \n";
- return;
- }
+ ASSERT_NE(inputFp, nullptr) << "Unable to open " << inputFile << " file for reading";
// Read file properties
- size_t fileSize = 0;
- fseek(inputFp, 0, SEEK_END);
- fileSize = ftell(inputFp);
- fseek(inputFp, 0, SEEK_SET);
+ struct stat buf;
+ stat(inputFile.c_str(), &buf);
+ size_t fileSize = buf.st_size;
int32_t fd = fileno(inputFp);
int32_t trackCount = extractObj->initExtractor(fd, fileSize);
- if (trackCount <= 0) {
- cout << "[ WARN ] Test Skipped. initExtractor failed\n";
- return;
- }
+ ASSERT_GT(trackCount, 0) << "initExtractor failed";
int32_t trackID = GetParam().second;
int32_t status = extractObj->extract(trackID);
- if (status != AMEDIA_OK) {
- cout << "[ WARN ] Test Skipped. Extraction failed \n";
- return;
- }
+ ASSERT_EQ(status, AMEDIA_OK) << "Extraction failed \n";
extractObj->deInitExtractor();
@@ -79,7 +70,8 @@
make_pair("bbb_8000hz_1ch_8kbps_amrnb_5mins.3gp", 0),
make_pair("bbb_16000hz_1ch_9kbps_amrwb_5mins.3gp", 0),
make_pair("bbb_44100hz_2ch_80kbps_vorbis_5mins.mp4", 0),
- make_pair("bbb_48000hz_2ch_100kbps_opus_5mins.webm", 0)));
+ make_pair("bbb_48000hz_2ch_100kbps_opus_5mins.webm",
+ 0)));
int main(int argc, char **argv) {
gEnv = new BenchmarkTestEnvironment();
diff --git a/media/tests/benchmark/tests/MuxerTest.cpp b/media/tests/benchmark/tests/MuxerTest.cpp
index e814f90..7b01732 100644
--- a/media/tests/benchmark/tests/MuxerTest.cpp
+++ b/media/tests/benchmark/tests/MuxerTest.cpp
@@ -21,8 +21,8 @@
#include <fstream>
#include <iostream>
-#include "Muxer.h"
#include "BenchmarkTestEnvironment.h"
+#include "Muxer.h"
#define OUTPUT_FILE_NAME "/data/local/tmp/mux.out"
@@ -53,49 +53,34 @@
ALOGV("Mux the samples given by extractor");
string inputFile = gEnv->getRes() + GetParam().first;
FILE *inputFp = fopen(inputFile.c_str(), "rb");
- if (!inputFp) {
- cout << "[ WARN ] Test Skipped. Unable to open input file for reading \n";
- return;
- }
+ ASSERT_NE(inputFp, nullptr) << "Unable to open " << inputFile << " file for reading";
+
string fmt = GetParam().second;
MUXER_OUTPUT_T outputFormat = getMuxerOutFormat(fmt);
- if (outputFormat == MUXER_OUTPUT_FORMAT_INVALID) {
- ALOGE("output format is MUXER_OUTPUT_FORMAT_INVALID");
- return;
- }
+ ASSERT_NE(outputFormat, MUXER_OUTPUT_FORMAT_INVALID) << "Invalid muxer output format";
Muxer *muxerObj = new Muxer();
+ ASSERT_NE(muxerObj, nullptr) << "Muxer creation failed";
+
Extractor *extractor = muxerObj->getExtractor();
- if (!extractor) {
- cout << "[ WARN ] Test Skipped. Extractor creation failed \n";
- return;
- }
+ ASSERT_NE(extractor, nullptr) << "Extractor creation failed";
// Read file properties
- size_t fileSize = 0;
- fseek(inputFp, 0, SEEK_END);
- fileSize = ftell(inputFp);
- fseek(inputFp, 0, SEEK_SET);
+ struct stat buf;
+ stat(inputFile.c_str(), &buf);
+ size_t fileSize = buf.st_size;
int32_t fd = fileno(inputFp);
int32_t trackCount = extractor->initExtractor(fd, fileSize);
- if (trackCount <= 0) {
- cout << "[ WARN ] Test Skipped. initExtractor failed\n";
- return;
- }
+ ASSERT_GT(trackCount, 0) << "initExtractor failed";
for (int curTrack = 0; curTrack < trackCount; curTrack++) {
int32_t status = extractor->setupTrackFormat(curTrack);
- if (status != 0) {
- cout << "[ WARN ] Test Skipped. Track Format invalid \n";
- return;
- }
+ ASSERT_EQ(status, 0) << "Track Format invalid";
uint8_t *inputBuffer = (uint8_t *)malloc(kMaxBufferSize);
- if (!inputBuffer) {
- std::cout << "[ WARN ] Test Skipped. Insufficient memory \n";
- return;
- }
+ ASSERT_NE(inputBuffer, nullptr) << "Insufficient memory";
+
// AMediaCodecBufferInfo : <size of frame> <flags> <presentationTimeUs> <offset>
vector<AMediaCodecBufferInfo> frameInfos;
AMediaCodecBufferInfo info;
@@ -106,11 +91,9 @@
status = extractor->getFrameSample(info);
if (status || !info.size) break;
// copy the meta data and buffer to be passed to muxer
- if (inputBufferOffset + info.size > kMaxBufferSize) {
- cout << "[ WARN ] Test Skipped. Memory allocated not sufficient\n";
- free(inputBuffer);
- return;
- }
+ ASSERT_LE(inputBufferOffset + info.size, kMaxBufferSize)
+ << "Memory allocated not sufficient";
+
memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(), info.size);
info.offset = inputBufferOffset;
frameInfos.push_back(info);
@@ -119,22 +102,16 @@
string outputFileName = OUTPUT_FILE_NAME;
FILE *outputFp = fopen(outputFileName.c_str(), "w+b");
- if (!outputFp) {
- cout << "[ WARN ] Test Skipped. Unable to open output file for writing \n";
- return;
- }
+ ASSERT_NE(outputFp, nullptr)
+ << "Unable to open output file" << outputFileName << " for writing";
+
int32_t fd = fileno(outputFp);
status = muxerObj->initMuxer(fd, outputFormat);
- if (status != 0) {
- cout << "[ WARN ] Test Skipped. initMuxer failed\n";
- return;
- }
+ ASSERT_EQ(status, 0) << "initMuxer failed";
status = muxerObj->mux(inputBuffer, frameInfos);
- if (status != 0) {
- cout << "[ WARN ] Test Skipped. Mux failed \n";
- return;
- }
+ ASSERT_EQ(status, 0) << "Mux failed";
+
muxerObj->deInitMuxer();
muxerObj->dumpStatistics(GetParam().first + "." + fmt.c_str());
free(inputBuffer);
diff --git a/media/utils/Android.bp b/media/utils/Android.bp
index 5047b19..b7037e9 100644
--- a/media/utils/Android.bp
+++ b/media/utils/Android.bp
@@ -29,6 +29,7 @@
"libc_malloc_debug_backtrace",
],
shared_libs: [
+ "libaudioutils", // for clock.h
"libbinder",
"libcutils",
"liblog",
diff --git a/media/utils/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp
index a77311e..87ea084 100644
--- a/media/utils/ServiceUtilities.cpp
+++ b/media/utils/ServiceUtilities.cpp
@@ -16,6 +16,7 @@
#define LOG_TAG "ServiceUtilities"
+#include <audio_utils/clock.h>
#include <binder/AppOpsManager.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
@@ -24,6 +25,7 @@
#include <iterator>
#include <algorithm>
+#include <pwd.h>
/* When performing permission checks we do not use permission cache for
* runtime permissions (protection level dangerous) as they may change at
@@ -143,6 +145,15 @@
return ok;
}
+bool captureVoiceCommunicationOutputAllowed(pid_t pid, uid_t uid) {
+ if (isAudioServerOrRootUid(uid)) return true;
+ static const String16 sCaptureVoiceCommOutput(
+ "android.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT");
+ bool ok = PermissionCache::checkPermission(sCaptureVoiceCommOutput, pid, uid);
+ if (!ok) ALOGE("Request requires android.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT");
+ return ok;
+}
+
bool captureHotwordAllowed(const String16& opPackageName, pid_t pid, uid_t uid) {
// CAPTURE_AUDIO_HOTWORD permission implies RECORD_AUDIO permission
bool ok = recordingAllowed(opPackageName, pid, uid);
@@ -329,4 +340,130 @@
}
}
+// How long we hold info before we re-fetch it (24 hours) if we found it previously.
+static constexpr nsecs_t INFO_EXPIRATION_NS = 24 * 60 * 60 * NANOS_PER_SECOND;
+// Maximum info records we retain before clearing everything.
+static constexpr size_t INFO_CACHE_MAX = 1000;
+
+// The original code is from MediaMetricsService.cpp.
+mediautils::UidInfo::Info mediautils::UidInfo::getInfo(uid_t uid)
+{
+ const nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
+ struct mediautils::UidInfo::Info info;
+ {
+ std::lock_guard _l(mLock);
+ auto it = mInfoMap.find(uid);
+ if (it != mInfoMap.end()) {
+ info = it->second;
+ ALOGV("%s: uid %d expiration %lld now %lld",
+ __func__, uid, (long long)info.expirationNs, (long long)now);
+ if (info.expirationNs <= now) {
+ // purge the stale entry and fall into re-fetching
+ ALOGV("%s: entry for uid %d expired, now %lld",
+ __func__, uid, (long long)now);
+ mInfoMap.erase(it);
+ info.uid = (uid_t)-1; // this is always fully overwritten
+ }
+ }
+ }
+
+ // if we did not find it in our map, look it up
+ if (info.uid == (uid_t)(-1)) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<content::pm::IPackageManagerNative> package_mgr;
+ if (sm.get() == nullptr) {
+ ALOGE("%s: Cannot find service manager", __func__);
+ } else {
+ sp<IBinder> binder = sm->getService(String16("package_native"));
+ if (binder.get() == nullptr) {
+ ALOGE("%s: Cannot find package_native", __func__);
+ } else {
+ package_mgr = interface_cast<content::pm::IPackageManagerNative>(binder);
+ }
+ }
+
+ // find package name
+ std::string pkg;
+ if (package_mgr != nullptr) {
+ std::vector<std::string> names;
+ binder::Status status = package_mgr->getNamesForUids({(int)uid}, &names);
+ if (!status.isOk()) {
+ ALOGE("%s: getNamesForUids failed: %s",
+ __func__, status.exceptionMessage().c_str());
+ } else {
+ if (!names[0].empty()) {
+ pkg = names[0].c_str();
+ }
+ }
+ }
+
+ if (pkg.empty()) {
+ struct passwd pw{}, *result;
+ char buf[8192]; // extra buffer space - should exceed what is
+ // required in struct passwd_pw (tested),
+ // and even then this is only used in backup
+ // when the package manager is unavailable.
+ if (getpwuid_r(uid, &pw, buf, sizeof(buf), &result) == 0
+ && result != nullptr
+ && result->pw_name != nullptr) {
+ pkg = result->pw_name;
+ }
+ }
+
+ // strip any leading "shared:" strings that came back
+ if (pkg.compare(0, 7, "shared:") == 0) {
+ pkg.erase(0, 7);
+ }
+
+ // determine how pkg was installed and the versionCode
+ std::string installer;
+ int64_t versionCode = 0;
+ bool notFound = false;
+ if (pkg.empty()) {
+ pkg = std::to_string(uid); // not found
+ notFound = true;
+ } else if (strchr(pkg.c_str(), '.') == nullptr) {
+ // not of form 'com.whatever...'; assume internal
+ // so we don't need to look it up in package manager.
+ } else if (strncmp(pkg.c_str(), "android.", 8) == 0) {
+ // android.* packages are assumed fine
+ } else if (package_mgr.get() != nullptr) {
+ String16 pkgName16(pkg.c_str());
+ binder::Status status = package_mgr->getInstallerForPackage(pkgName16, &installer);
+ if (!status.isOk()) {
+ ALOGE("%s: getInstallerForPackage failed: %s",
+ __func__, status.exceptionMessage().c_str());
+ }
+
+ // skip if we didn't get an installer
+ if (status.isOk()) {
+ status = package_mgr->getVersionCodeForPackage(pkgName16, &versionCode);
+ if (!status.isOk()) {
+ ALOGE("%s: getVersionCodeForPackage failed: %s",
+ __func__, status.exceptionMessage().c_str());
+ }
+ }
+
+ ALOGV("%s: package '%s' installed by '%s' versioncode %lld",
+ __func__, pkg.c_str(), installer.c_str(), (long long)versionCode);
+ }
+
+ // add it to the map, to save a subsequent lookup
+ std::lock_guard _l(mLock);
+ // first clear if we have too many cached elements. This would be rare.
+ if (mInfoMap.size() >= INFO_CACHE_MAX) mInfoMap.clear();
+
+ // always overwrite
+ info.uid = uid;
+ info.package = std::move(pkg);
+ info.installer = std::move(installer);
+ info.versionCode = versionCode;
+ info.expirationNs = now + (notFound ? 0 : INFO_EXPIRATION_NS);
+ ALOGV("%s: adding uid %d package '%s' expirationNs: %lld",
+ __func__, uid, info.package.c_str(), (long long)info.expirationNs);
+ mInfoMap[uid] = info;
+ }
+ return info;
+}
+
} // namespace android
diff --git a/media/utils/include/mediautils/ServiceUtilities.h b/media/utils/include/mediautils/ServiceUtilities.h
index e467058..212599a 100644
--- a/media/utils/include/mediautils/ServiceUtilities.h
+++ b/media/utils/include/mediautils/ServiceUtilities.h
@@ -28,6 +28,7 @@
#include <map>
#include <optional>
#include <string>
+#include <unordered_map>
#include <vector>
namespace android {
@@ -82,6 +83,7 @@
void finishRecording(const String16& opPackageName, uid_t uid);
bool captureAudioOutputAllowed(pid_t pid, uid_t uid);
bool captureMediaOutputAllowed(pid_t pid, uid_t uid);
+bool captureVoiceCommunicationOutputAllowed(pid_t pid, uid_t uid);
bool captureHotwordAllowed(const String16& opPackageName, pid_t pid, uid_t uid);
bool settingsAllowed();
bool modifyAudioRoutingAllowed();
@@ -118,6 +120,40 @@
using Packages = std::vector<Package>;
std::map<uid_t, Packages> mDebugLog;
};
-}
+
+namespace mediautils {
+
+/**
+ * This class is used to retrieve (and cache) package information
+ * for a given uid.
+ */
+class UidInfo {
+public:
+ struct Info {
+ uid_t uid = -1; // uid used for lookup.
+ std::string package; // package name.
+ std::string installer; // installer for the package (e.g. preload, play store).
+ int64_t versionCode = 0; // reported version code.
+ int64_t expirationNs = 0; // after this time in SYSTEM_TIME_REALTIME we refetch.
+ };
+
+ /**
+ * Returns the package information for a UID.
+ *
+ * The package name will be the uid if we cannot find the associated name.
+ *
+ * \param uid is the uid of the app or service.
+ */
+ Info getInfo(uid_t uid);
+
+private:
+ std::mutex mLock;
+ // TODO: use concurrent hashmap with striped lock.
+ std::unordered_map<uid_t, Info> mInfoMap; // GUARDED_BY(mLock)
+};
+
+} // namespace mediautils
+
+} // namespace android
#endif // ANDROID_MEDIAUTILS_SERVICEUTILITIES_H
diff --git a/services/audioflinger/Android.bp b/services/audioflinger/Android.bp
index de8c7e7..f06d026 100644
--- a/services/audioflinger/Android.bp
+++ b/services/audioflinger/Android.bp
@@ -3,12 +3,15 @@
cc_library_shared {
name: "libaudioflinger",
+ tidy: false, // b/146435095, segmentation fault with Effects.cpp
+
srcs: [
"AudioFlinger.cpp",
"AudioHwDevice.cpp",
"AudioStreamOut.cpp",
"AudioWatchdog.cpp",
"BufLog.cpp",
+ "DeviceEffectManager.cpp",
"Effects.cpp",
"FastCapture.cpp",
"FastCaptureDumpState.cpp",
@@ -62,6 +65,7 @@
],
header_libs: [
+ "libaudiohal_headers",
"libmedia_headers",
],
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 0911a1f..5c5c5bb 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -40,6 +40,7 @@
#include <media/audiohal/DevicesFactoryHalInterface.h>
#include <media/audiohal/EffectsFactoryHalInterface.h>
#include <media/AudioParameter.h>
+#include <media/MediaMetricsItem.h>
#include <media/TypeConverter.h>
#include <memunreachable/memunreachable.h>
#include <utils/String16.h>
@@ -170,6 +171,7 @@
mClientSharedHeapSize(kMinimumClientSharedHeapSizeBytes),
mGlobalEffectEnableTime(0),
mPatchPanel(this),
+ mDeviceEffectManager(this),
mSystemReady(false)
{
// unsigned instead of audio_unique_id_use_t, because ++ operator is unavailable for enum
@@ -197,6 +199,11 @@
std::vector<pid_t> halPids;
mDevicesFactoryHal->getHalPids(&halPids);
TimeCheck::setAudioHalPids(halPids);
+
+ // Notify that we have started (also called when audioserver service restarts)
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_CTOR)
+ .record();
}
void AudioFlinger::onFirstRef()
@@ -347,6 +354,9 @@
thread->configure(&localAttr, streamType, actualSessionId, callback, *deviceId, portId);
*handle = portId;
*sessionId = actualSessionId;
+ config->sample_rate = thread->sampleRate();
+ config->channel_mask = thread->channelMask();
+ config->format = thread->format();
} else {
if (direction == MmapStreamInterface::DIRECTION_OUTPUT) {
AudioSystem::releaseOutput(portId);
@@ -382,6 +392,24 @@
}
}
+status_t AudioFlinger::addEffectToHal(audio_port_handle_t deviceId,
+ audio_module_handle_t hwModuleId, sp<EffectHalInterface> effect) {
+ AudioHwDevice *audioHwDevice = mAudioHwDevs.valueFor(hwModuleId);
+ if (audioHwDevice == nullptr) {
+ return NO_INIT;
+ }
+ return audioHwDevice->hwDevice()->addDeviceEffect(deviceId, effect);
+}
+
+status_t AudioFlinger::removeEffectFromHal(audio_port_handle_t deviceId,
+ audio_module_handle_t hwModuleId, sp<EffectHalInterface> effect) {
+ AudioHwDevice *audioHwDevice = mAudioHwDevs.valueFor(hwModuleId);
+ if (audioHwDevice == nullptr) {
+ return NO_INIT;
+ }
+ return audioHwDevice->hwDevice()->removeDeviceEffect(deviceId, effect);
+}
+
static const char * const audio_interfaces[] = {
AUDIO_HARDWARE_MODULE_ID_PRIMARY,
AUDIO_HARDWARE_MODULE_ID_A2DP,
@@ -422,31 +450,32 @@
void AudioFlinger::dumpClients(int fd, const Vector<String16>& args __unused)
{
- const size_t SIZE = 256;
- char buffer[SIZE];
String8 result;
result.append("Clients:\n");
for (size_t i = 0; i < mClients.size(); ++i) {
sp<Client> client = mClients.valueAt(i).promote();
if (client != 0) {
- snprintf(buffer, SIZE, " pid: %d\n", client->pid());
- result.append(buffer);
+ result.appendFormat(" pid: %d\n", client->pid());
}
}
result.append("Notification Clients:\n");
+ result.append(" pid uid name\n");
for (size_t i = 0; i < mNotificationClients.size(); ++i) {
- snprintf(buffer, SIZE, " pid: %d\n", mNotificationClients.keyAt(i));
- result.append(buffer);
+ const pid_t pid = mNotificationClients[i]->getPid();
+ const uid_t uid = mNotificationClients[i]->getUid();
+ const mediautils::UidInfo::Info info = mUidInfo.getInfo(uid);
+ result.appendFormat("%6d %6u %s\n", pid, uid, info.package.c_str());
}
result.append("Global session refs:\n");
- result.append(" session pid count\n");
+ result.append(" session cnt pid uid name\n");
for (size_t i = 0; i < mAudioSessionRefs.size(); i++) {
AudioSessionRef *r = mAudioSessionRefs[i];
- snprintf(buffer, SIZE, " %7d %5d %5d\n", r->mSessionid, r->mPid, r->mCnt);
- result.append(buffer);
+ const mediautils::UidInfo::Info info = mUidInfo.getInfo(r->mUid);
+ result.appendFormat(" %7d %4d %7d %6u %s\n", r->mSessionid, r->mCnt, r->mPid,
+ r->mUid, info.package.c_str());
}
write(fd, result.string(), result.size());
}
@@ -558,6 +587,8 @@
mPatchPanel.dump(fd);
+ mDeviceEffectManager.dump(fd);
+
// dump external setParameters
auto dumpLogger = [fd](SimpleLog& logger, const char* name) {
dprintf(fd, "\n%s setParameters:\n", name);
@@ -1582,11 +1613,6 @@
AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_HW_GET_INPUT_BUFFER_SIZE;
- audio_config_t config, proposed;
- memset(&proposed, 0, sizeof(proposed));
- proposed.sample_rate = sampleRate;
- proposed.channel_mask = channelMask;
- proposed.format = format;
sp<DeviceHalInterface> dev = mPrimaryHardwareDev->hwDevice();
std::vector<audio_channel_mask_t> channelMasks = {channelMask};
@@ -1610,12 +1636,16 @@
mHardwareStatus = AUDIO_HW_IDLE;
+ // Change parameters of the configuration each iteration until we find a
+ // configuration that the device will support.
+ audio_config_t config = AUDIO_CONFIG_INITIALIZER;
for (auto testChannelMask : channelMasks) {
config.channel_mask = testChannelMask;
for (auto testFormat : formats) {
config.format = testFormat;
for (auto testSampleRate : sampleRates) {
config.sample_rate = testSampleRate;
+
size_t bytes = 0;
status_t result = dev->getInputBufferSize(&config, &bytes);
if (result != OK || bytes == 0) {
@@ -1695,13 +1725,16 @@
return;
}
pid_t pid = IPCThreadState::self()->getCallingPid();
+ const uid_t uid = IPCThreadState::self()->getCallingUid();
{
Mutex::Autolock _cl(mClientLock);
if (mNotificationClients.indexOfKey(pid) < 0) {
sp<NotificationClient> notificationClient = new NotificationClient(this,
client,
- pid);
- ALOGV("registerClient() client %p, pid %d", notificationClient.get(), pid);
+ pid,
+ uid);
+ ALOGV("registerClient() client %p, pid %d, uid %u",
+ notificationClient.get(), pid, uid);
mNotificationClients.add(pid, notificationClient);
@@ -1841,8 +1874,9 @@
AudioFlinger::NotificationClient::NotificationClient(const sp<AudioFlinger>& audioFlinger,
const sp<IAudioFlingerClient>& client,
- pid_t pid)
- : mAudioFlinger(audioFlinger), mPid(pid), mAudioFlingerClient(client)
+ pid_t pid,
+ uid_t uid)
+ : mAudioFlinger(audioFlinger), mPid(pid), mUid(uid), mAudioFlingerClient(client)
{
}
@@ -2869,14 +2903,18 @@
return nextUniqueId(use);
}
-void AudioFlinger::acquireAudioSessionId(audio_session_t audioSession, pid_t pid)
+void AudioFlinger::acquireAudioSessionId(
+ audio_session_t audioSession, pid_t pid, uid_t uid)
{
Mutex::Autolock _l(mLock);
pid_t caller = IPCThreadState::self()->getCallingPid();
ALOGV("acquiring %d from %d, for %d", audioSession, caller, pid);
const uid_t callerUid = IPCThreadState::self()->getCallingUid();
- if (pid != -1 && isAudioServerUid(callerUid)) { // check must match releaseAudioSessionId()
- caller = pid;
+ if (pid != (pid_t)-1 && isAudioServerOrMediaServerUid(callerUid)) {
+ caller = pid; // check must match releaseAudioSessionId()
+ }
+ if (uid == (uid_t)-1 || !isAudioServerOrMediaServerUid(callerUid)) {
+ uid = callerUid;
}
{
@@ -2900,7 +2938,7 @@
return;
}
}
- mAudioSessionRefs.push(new AudioSessionRef(audioSession, caller));
+ mAudioSessionRefs.push(new AudioSessionRef(audioSession, caller, uid));
ALOGV(" added new entry for %d", audioSession);
}
@@ -2912,8 +2950,8 @@
pid_t caller = IPCThreadState::self()->getCallingPid();
ALOGV("releasing %d from %d for %d", audioSession, caller, pid);
const uid_t callerUid = IPCThreadState::self()->getCallingUid();
- if (pid != -1 && isAudioServerUid(callerUid)) { // check must match acquireAudioSessionId()
- caller = pid;
+ if (pid != (pid_t)-1 && isAudioServerOrMediaServerUid(callerUid)) {
+ caller = pid; // check must match acquireAudioSessionId()
}
size_t num = mAudioSessionRefs.size();
for (size_t i = 0; i < num; i++) {
@@ -2993,7 +3031,7 @@
for (size_t i = 0; i < chains.size(); i++) {
sp<EffectChain> ec = chains[i];
int sessionid = ec->sessionId();
- sp<ThreadBase> t = ec->mThread.promote();
+ sp<ThreadBase> t = ec->thread().promote();
if (t == 0) {
continue;
}
@@ -3016,7 +3054,7 @@
effect->unPin();
t->removeEffect_l(effect, /*release*/ true);
if (effect->purgeHandles()) {
- t->checkSuspendOnEffectEnabled_l(effect, false, effect->sessionId());
+ effect->checkSuspendOnEffectEnabled(false, true /*threadLocked*/);
}
removedEffects.push_back(effect);
}
@@ -3316,7 +3354,7 @@
int32_t priority,
audio_io_handle_t io,
audio_session_t sessionId,
- const AudioDeviceTypeAddr& device __unused,
+ const AudioDeviceTypeAddr& device,
const String16& opPackageName,
pid_t pid,
status_t *status,
@@ -3380,7 +3418,6 @@
lStatus = BAD_VALUE;
goto Exit;
}
- //TODO: add check on device ID when added to arguments
} else {
// general sessionId.
@@ -3433,6 +3470,23 @@
Mutex::Autolock _l(mLock);
+ if (sessionId == AUDIO_SESSION_DEVICE) {
+ sp<Client> client = registerPid(pid);
+ ALOGV("%s device type %d address %s", __func__, device.mType, device.getAddress());
+ handle = mDeviceEffectManager.createEffect_l(
+ &desc, device, client, effectClient, mPatchPanel.patches_l(),
+ enabled, &lStatus);
+ if (lStatus != NO_ERROR && lStatus != ALREADY_EXISTS) {
+ // remove local strong reference to Client with mClientLock held
+ Mutex::Autolock _cl(mClientLock);
+ client.clear();
+ } else {
+ // handle must be valid here, but check again to be safe.
+ if (handle.get() != nullptr && id != nullptr) *id = handle->id();
+ }
+ goto Register;
+ }
+
// If output is not specified try to find a matching audio session ID in one of the
// output threads.
// If output is 0 here, sessionId is neither SESSION_OUTPUT_STAGE nor SESSION_OUTPUT_MIX
@@ -3527,9 +3581,10 @@
}
}
+Register:
if (lStatus == NO_ERROR || lStatus == ALREADY_EXISTS) {
// Check CPU and memory usage
- sp<EffectModule> effect = handle->effect().promote();
+ sp<EffectBase> effect = handle->effect().promote();
if (effect != nullptr) {
status_t rStatus = effect->updatePolicyState();
if (rStatus != NO_ERROR) {
@@ -3639,7 +3694,7 @@
// if the move request is not received from audio policy manager, the effect must be
// re-registered with the new strategy and output
if (dstChain == 0) {
- dstChain = effect->chain().promote();
+ dstChain = effect->callback()->chain().promote();
if (dstChain == 0) {
ALOGW("moveEffectChain_l() cannot get chain from effect %p", effect.get());
status = NO_INIT;
@@ -3689,7 +3744,7 @@
goto Exit;
}
- dstChain = effect->chain().promote();
+ dstChain = effect->callback()->chain().promote();
if (dstChain == 0) {
thread->addEffect_l(effect);
status = INVALID_OPERATION;
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index eb851ec..a16fa94 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -71,6 +71,7 @@
#include <media/DeviceDescriptorBase.h>
#include <media/ExtendedAudioBufferProvider.h>
#include <media/VolumeShaper.h>
+#include <mediautils/ServiceUtilities.h>
#include <audio_utils/clock.h>
#include <audio_utils/FdToString.h>
@@ -213,7 +214,7 @@
// This is the binder API. For the internal API see nextUniqueId().
virtual audio_unique_id_t newAudioUniqueId(audio_unique_id_use_t use);
- virtual void acquireAudioSessionId(audio_session_t audioSession, pid_t pid);
+ void acquireAudioSessionId(audio_session_t audioSession, pid_t pid, uid_t uid) override;
virtual void releaseAudioSessionId(audio_session_t audioSession, pid_t pid);
@@ -308,6 +309,12 @@
static int onExternalVibrationStart(const sp<os::ExternalVibration>& externalVibration);
static void onExternalVibrationStop(const sp<os::ExternalVibration>& externalVibration);
+
+ status_t addEffectToHal(audio_port_handle_t deviceId,
+ audio_module_handle_t hwModuleId, sp<EffectHalInterface> effect);
+ status_t removeEffectFromHal(audio_port_handle_t deviceId,
+ audio_module_handle_t hwModuleId, sp<EffectHalInterface> effect);
+
private:
// FIXME The 400 is temporarily too high until a leak of writers in media.log is fixed.
static const size_t kLogMemorySize = 400 * 1024;
@@ -336,7 +343,10 @@
virtual ~SyncEvent() {}
- void trigger() { Mutex::Autolock _l(mLock); if (mCallback) mCallback(this); }
+ void trigger() {
+ Mutex::Autolock _l(mLock);
+ if (mCallback) mCallback(wp<SyncEvent>(this));
+ }
bool isCancelled() const { Mutex::Autolock _l(mLock); return (mCallback == NULL); }
void cancel() { Mutex::Autolock _l(mLock); mCallback = NULL; }
AudioSystem::sync_event_t type() const { return mType; }
@@ -474,10 +484,13 @@
public:
NotificationClient(const sp<AudioFlinger>& audioFlinger,
const sp<IAudioFlingerClient>& client,
- pid_t pid);
+ pid_t pid,
+ uid_t uid);
virtual ~NotificationClient();
sp<IAudioFlingerClient> audioFlingerClient() const { return mAudioFlingerClient; }
+ pid_t getPid() const { return mPid; }
+ uid_t getUid() const { return mUid; }
// IBinder::DeathRecipient
virtual void binderDied(const wp<IBinder>& who);
@@ -487,6 +500,7 @@
const sp<AudioFlinger> mAudioFlinger;
const pid_t mPid;
+ const uid_t mUid;
const sp<IAudioFlingerClient> mAudioFlingerClient;
};
@@ -533,9 +547,14 @@
class AsyncCallbackThread;
class Track;
class RecordTrack;
+ class EffectBase;
class EffectModule;
class EffectHandle;
class EffectChain;
+ class DeviceEffectProxy;
+ class DeviceEffectManager;
+ class PatchPanel;
+ class DeviceEffectManagerCallback;
struct AudioStreamIn;
struct TeePatch;
@@ -573,9 +592,11 @@
#include "Threads.h"
+#include "PatchPanel.h"
+
#include "Effects.h"
-#include "PatchPanel.h"
+#include "DeviceEffectManager.h"
// Find io handle by session id.
// Preference is given to an io handle with a matching effect chain to session id.
@@ -790,10 +811,11 @@
// for mAudioSessionRefs only
struct AudioSessionRef {
- AudioSessionRef(audio_session_t sessionid, pid_t pid) :
- mSessionid(sessionid), mPid(pid), mCnt(1) {}
+ AudioSessionRef(audio_session_t sessionid, pid_t pid, uid_t uid) :
+ mSessionid(sessionid), mPid(pid), mUid(uid), mCnt(1) {}
const audio_session_t mSessionid;
const pid_t mPid;
+ const uid_t mUid;
int mCnt;
};
@@ -921,11 +943,17 @@
PatchPanel mPatchPanel;
sp<EffectsFactoryHalInterface> mEffectsFactoryHal;
+ DeviceEffectManager mDeviceEffectManager;
+
bool mSystemReady;
+ mediautils::UidInfo mUidInfo;
+
SimpleLog mRejectedSetParameterLog;
SimpleLog mAppSetParameterLog;
SimpleLog mSystemSetParameterLog;
+
+ static inline constexpr const char *mMetricsId = AMEDIAMETRICS_KEY_AUDIO_FLINGER;
};
#undef INCLUDING_FROM_AUDIOFLINGER_H
diff --git a/services/audioflinger/DeviceEffectManager.cpp b/services/audioflinger/DeviceEffectManager.cpp
new file mode 100644
index 0000000..87a4c6e
--- /dev/null
+++ b/services/audioflinger/DeviceEffectManager.cpp
@@ -0,0 +1,277 @@
+/*
+**
+** Copyright 2019, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+
+#define LOG_TAG "AudioFlinger::DeviceEffectManager"
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <audio_utils/primitives.h>
+
+#include "AudioFlinger.h"
+#include <media/audiohal/EffectsFactoryHalInterface.h>
+
+// ----------------------------------------------------------------------------
+
+
+namespace android {
+
+void AudioFlinger::DeviceEffectManager::createAudioPatch(audio_patch_handle_t handle,
+ const PatchPanel::Patch& patch) {
+ ALOGV("%s handle %d mHalHandle %d num sinks %d device sink %08x",
+ __func__, handle, patch.mHalHandle,
+ patch.mAudioPatch.num_sinks,
+ patch.mAudioPatch.num_sinks > 0 ? patch.mAudioPatch.sinks[0].ext.device.type : 0);
+
+ mCommandThread->createAudioPatchCommand(handle, patch);
+}
+
+void AudioFlinger::DeviceEffectManager::onCreateAudioPatch(audio_patch_handle_t handle,
+ const PatchPanel::Patch& patch) {
+ ALOGV("%s handle %d mHalHandle %d device sink %08x",
+ __func__, handle, patch.mHalHandle,
+ patch.mAudioPatch.num_sinks > 0 ? patch.mAudioPatch.sinks[0].ext.device.type : 0);
+ Mutex::Autolock _l(mLock);
+ for (auto& effect : mDeviceEffects) {
+ status_t status = effect.second->onCreatePatch(handle, patch);
+ ALOGV("%s Effect onCreatePatch status %d", __func__, status);
+ ALOGW_IF(status == BAD_VALUE, "%s onCreatePatch error %d", __func__, status);
+ }
+}
+
+void AudioFlinger::DeviceEffectManager::releaseAudioPatch(audio_patch_handle_t handle) {
+ ALOGV("%s", __func__);
+ mCommandThread->releaseAudioPatchCommand(handle);
+}
+
+void AudioFlinger::DeviceEffectManager::onReleaseAudioPatch(audio_patch_handle_t handle) {
+ ALOGV("%s", __func__);
+ Mutex::Autolock _l(mLock);
+ for (auto& effect : mDeviceEffects) {
+ effect.second->onReleasePatch(handle);
+ }
+}
+
+// DeviceEffectManager::createEffect_l() must be called with AudioFlinger::mLock held
+sp<AudioFlinger::EffectHandle> AudioFlinger::DeviceEffectManager::createEffect_l(
+ effect_descriptor_t *descriptor,
+ const AudioDeviceTypeAddr& device,
+ const sp<AudioFlinger::Client>& client,
+ const sp<IEffectClient>& effectClient,
+ const std::map<audio_patch_handle_t, PatchPanel::Patch>& patches,
+ int *enabled,
+ status_t *status) {
+ sp<DeviceEffectProxy> effect;
+ sp<EffectHandle> handle;
+ status_t lStatus;
+
+ lStatus = checkEffectCompatibility(descriptor);
+ if (lStatus != NO_ERROR) {
+ *status = lStatus;
+ return handle;
+ }
+
+ {
+ Mutex::Autolock _l(mLock);
+ auto iter = mDeviceEffects.find(device);
+ if (iter != mDeviceEffects.end()) {
+ effect = iter->second;
+ } else {
+ effect = new DeviceEffectProxy(device, mMyCallback,
+ descriptor, mAudioFlinger.nextUniqueId(AUDIO_UNIQUE_ID_USE_EFFECT));
+ }
+ // create effect handle and connect it to effect module
+ handle = new EffectHandle(effect, client, effectClient, 0 /*priority*/);
+ lStatus = handle->initCheck();
+ if (lStatus == NO_ERROR) {
+ lStatus = effect->addHandle(handle.get());
+ if (lStatus == NO_ERROR) {
+ effect->init(patches);
+ mDeviceEffects.emplace(device, effect);
+ }
+ }
+ }
+ if (enabled != NULL) {
+ *enabled = (int)effect->isEnabled();
+ }
+ *status = lStatus;
+ return handle;
+}
+
+status_t AudioFlinger::DeviceEffectManager::checkEffectCompatibility(
+ const effect_descriptor_t *desc) {
+
+ if ((desc->flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_PRE_PROC
+ && (desc->flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_POST_PROC) {
+ ALOGW("%s() non pre/post processing device effect %s", __func__, desc->name);
+ return BAD_VALUE;
+ }
+
+ return NO_ERROR;
+}
+
+status_t AudioFlinger::DeviceEffectManager::createEffectHal(
+ const effect_uuid_t *pEffectUuid, int32_t sessionId, int32_t deviceId,
+ sp<EffectHalInterface> *effect) {
+ status_t status = NO_INIT;
+ sp<EffectsFactoryHalInterface> effectsFactory = mAudioFlinger.getEffectsFactory();
+ if (effectsFactory != 0) {
+ status = effectsFactory->createEffect(
+ pEffectUuid, sessionId, AUDIO_IO_HANDLE_NONE, deviceId, effect);
+ }
+ return status;
+}
+
+void AudioFlinger::DeviceEffectManager::dump(int fd) {
+ const bool locked = dumpTryLock(mLock);
+ if (!locked) {
+ String8 result("DeviceEffectManager may be deadlocked\n");
+ write(fd, result.string(), result.size());
+ }
+
+ write(fd, "\nDevice Effects:\n", sizeof("\nDevice Effects:\n"));
+ for (const auto& iter : mDeviceEffects) {
+ String8 outStr;
+ outStr.appendFormat("%*sEffect for device %s address %s:\n", 2, "",
+ ::android::toString(iter.first.mType).c_str(), iter.first.getAddress());
+ write(fd, outStr.string(), outStr.size());
+ iter.second->dump(fd, 4);
+ }
+
+ if (locked) {
+ mLock.unlock();
+ }
+}
+
+
+size_t AudioFlinger::DeviceEffectManager::removeEffect(const sp<DeviceEffectProxy>& effect)
+{
+ Mutex::Autolock _l(mLock);
+ mDeviceEffects.erase(effect->device());
+ return mDeviceEffects.size();
+}
+
+bool AudioFlinger::DeviceEffectManagerCallback::disconnectEffectHandle(
+ EffectHandle *handle, bool unpinIfLast) {
+ sp<EffectBase> effectBase = handle->effect().promote();
+ if (effectBase == nullptr) {
+ return false;
+ }
+
+ sp<DeviceEffectProxy> effect = effectBase->asDeviceEffectProxy();
+ if (effect == nullptr) {
+ return false;
+ }
+ // restore suspended effects if the disconnected handle was enabled and the last one.
+ bool remove = (effect->removeHandle(handle) == 0) && (!effect->isPinned() || unpinIfLast);
+ if (remove) {
+ mManager.removeEffect(effect);
+ if (handle->enabled()) {
+ effectBase->checkSuspendOnEffectEnabled(false, false /*threadLocked*/);
+ }
+ }
+ return true;
+}
+
+// ----------- DeviceEffectManager::CommandThread implementation ----------
+
+
+AudioFlinger::DeviceEffectManager::CommandThread::~CommandThread()
+{
+ Mutex::Autolock _l(mLock);
+ mCommands.clear();
+}
+
+void AudioFlinger::DeviceEffectManager::CommandThread::onFirstRef()
+{
+ run("DeviceEffectManage_CommandThread", ANDROID_PRIORITY_AUDIO);
+}
+
+bool AudioFlinger::DeviceEffectManager::CommandThread::threadLoop()
+{
+ mLock.lock();
+ while (!exitPending())
+ {
+ while (!mCommands.empty() && !exitPending()) {
+ sp<Command> command = mCommands.front();
+ mCommands.pop_front();
+ mLock.unlock();
+
+ switch (command->mCommand) {
+ case CREATE_AUDIO_PATCH: {
+ CreateAudioPatchData *data = (CreateAudioPatchData *)command->mData.get();
+ ALOGV("CommandThread() processing create audio patch handle %d", data->mHandle);
+ mManager.onCreateAudioPatch(data->mHandle, data->mPatch);
+ } break;
+ case RELEASE_AUDIO_PATCH: {
+ ReleaseAudioPatchData *data = (ReleaseAudioPatchData *)command->mData.get();
+ ALOGV("CommandThread() processing release audio patch handle %d", data->mHandle);
+ mManager.onReleaseAudioPatch(data->mHandle);
+ } break;
+ default:
+ ALOGW("CommandThread() unknown command %d", command->mCommand);
+ }
+ mLock.lock();
+ }
+
+ // At this stage we have either an empty command queue or the first command in the queue
+ // has a finite delay. So unless we are exiting it is safe to wait.
+ if (!exitPending()) {
+ ALOGV("CommandThread() going to sleep");
+ mWaitWorkCV.wait(mLock);
+ }
+ }
+ mLock.unlock();
+ return false;
+}
+
+void AudioFlinger::DeviceEffectManager::CommandThread::sendCommand(sp<Command> command) {
+ Mutex::Autolock _l(mLock);
+ mCommands.push_back(command);
+ mWaitWorkCV.signal();
+}
+
+void AudioFlinger::DeviceEffectManager::CommandThread::createAudioPatchCommand(
+ audio_patch_handle_t handle, const PatchPanel::Patch& patch)
+{
+ sp<Command> command = new Command(CREATE_AUDIO_PATCH, new CreateAudioPatchData(handle, patch));
+ ALOGV("CommandThread() adding create patch handle %d mHalHandle %d.", handle, patch.mHalHandle);
+ sendCommand(command);
+}
+
+void AudioFlinger::DeviceEffectManager::CommandThread::releaseAudioPatchCommand(
+ audio_patch_handle_t handle)
+{
+ sp<Command> command = new Command(RELEASE_AUDIO_PATCH, new ReleaseAudioPatchData(handle));
+ ALOGV("CommandThread() adding release patch");
+ sendCommand(command);
+}
+
+void AudioFlinger::DeviceEffectManager::CommandThread::exit()
+{
+ ALOGV("CommandThread::exit");
+ {
+ AutoMutex _l(mLock);
+ requestExit();
+ mWaitWorkCV.signal();
+ }
+ // Note that we can call it from the thread loop if all other references have been released
+ // but it will safely return WOULD_BLOCK in this case
+ requestExitAndWait();
+}
+
+} // namespace android
diff --git a/services/audioflinger/DeviceEffectManager.h b/services/audioflinger/DeviceEffectManager.h
new file mode 100644
index 0000000..14ff14d
--- /dev/null
+++ b/services/audioflinger/DeviceEffectManager.h
@@ -0,0 +1,203 @@
+/*
+**
+** Copyright 2019, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef INCLUDING_FROM_AUDIOFLINGER_H
+ #error This header file should only be included from AudioFlinger.h
+#endif
+
+// DeviceEffectManager is concealed within AudioFlinger, their lifetimes are the same.
+class DeviceEffectManager {
+public:
+ explicit DeviceEffectManager(AudioFlinger* audioFlinger)
+ : mCommandThread(new CommandThread(*this)), mAudioFlinger(*audioFlinger),
+ mMyCallback(new DeviceEffectManagerCallback(this)) {}
+
+ ~DeviceEffectManager() {
+ mCommandThread->exit();
+ }
+
+ sp<EffectHandle> createEffect_l(effect_descriptor_t *descriptor,
+ const AudioDeviceTypeAddr& device,
+ const sp<AudioFlinger::Client>& client,
+ const sp<IEffectClient>& effectClient,
+ const std::map<audio_patch_handle_t, PatchPanel::Patch>& patches,
+ int *enabled,
+ status_t *status);
+ void createAudioPatch(audio_patch_handle_t handle, const PatchPanel::Patch& patch);
+ void releaseAudioPatch(audio_patch_handle_t handle);
+
+ size_t removeEffect(const sp<DeviceEffectProxy>& effect);
+ status_t createEffectHal(const effect_uuid_t *pEffectUuid,
+ int32_t sessionId, int32_t deviceId,
+ sp<EffectHalInterface> *effect);
+ status_t addEffectToHal(audio_port_handle_t deviceId, audio_module_handle_t hwModuleId,
+ sp<EffectHalInterface> effect) {
+ return mAudioFlinger.addEffectToHal(deviceId, hwModuleId, effect);
+ };
+ status_t removeEffectFromHal(audio_port_handle_t deviceId, audio_module_handle_t hwModuleId,
+ sp<EffectHalInterface> effect) {
+ return mAudioFlinger.removeEffectFromHal(deviceId, hwModuleId, effect);
+ };
+
+ AudioFlinger& audioFlinger() const { return mAudioFlinger; }
+
+ void dump(int fd);
+
+private:
+
+ // Thread to execute create and release patch commands asynchronously. This is needed because
+ // PatchPanel::createAudioPatch and releaseAudioPatch are executed from audio policy service
+ // with mutex locked and effect management requires to call back into audio policy service
+ class Command;
+ class CommandThread : public Thread {
+ public:
+
+ enum {
+ CREATE_AUDIO_PATCH,
+ RELEASE_AUDIO_PATCH,
+ };
+
+ CommandThread(DeviceEffectManager& manager)
+ : Thread(false), mManager(manager) {}
+ ~CommandThread() override;
+
+ // Thread virtuals
+ void onFirstRef() override;
+ bool threadLoop() override;
+
+ void exit();
+
+ void createAudioPatchCommand(audio_patch_handle_t handle,
+ const PatchPanel::Patch& patch);
+ void releaseAudioPatchCommand(audio_patch_handle_t handle);
+
+ private:
+ class CommandData;
+
+ // descriptor for requested tone playback event
+ class Command: public RefBase {
+ public:
+ Command() = default;
+ Command(int command, sp<CommandData> data)
+ : mCommand(command), mData(data) {}
+
+ int mCommand = -1;
+ sp<CommandData> mData;
+ };
+
+ class CommandData: public RefBase {
+ public:
+ virtual ~CommandData() = default;
+ };
+
+ class CreateAudioPatchData : public CommandData {
+ public:
+ CreateAudioPatchData(audio_patch_handle_t handle, const PatchPanel::Patch& patch)
+ : mHandle(handle), mPatch(patch) {}
+
+ audio_patch_handle_t mHandle;
+ const PatchPanel::Patch mPatch;
+ };
+
+ class ReleaseAudioPatchData : public CommandData {
+ public:
+ ReleaseAudioPatchData(audio_patch_handle_t handle)
+ : mHandle(handle) {}
+
+ audio_patch_handle_t mHandle;
+ };
+
+ void sendCommand(sp<Command> command);
+
+ Mutex mLock;
+ Condition mWaitWorkCV;
+ std::deque <sp<Command>> mCommands; // list of pending commands
+ DeviceEffectManager& mManager;
+ };
+
+ void onCreateAudioPatch(audio_patch_handle_t handle, const PatchPanel::Patch& patch);
+ void onReleaseAudioPatch(audio_patch_handle_t handle);
+
+ status_t checkEffectCompatibility(const effect_descriptor_t *desc);
+
+ Mutex mLock;
+ sp<CommandThread> mCommandThread;
+ AudioFlinger &mAudioFlinger;
+ const sp<DeviceEffectManagerCallback> mMyCallback;
+ std::map<AudioDeviceTypeAddr, sp<DeviceEffectProxy>> mDeviceEffects;
+};
+
+class DeviceEffectManagerCallback : public EffectCallbackInterface {
+public:
+ DeviceEffectManagerCallback(DeviceEffectManager *manager)
+ : mManager(*manager) {}
+
+ status_t createEffectHal(const effect_uuid_t *pEffectUuid,
+ int32_t sessionId, int32_t deviceId,
+ sp<EffectHalInterface> *effect) override {
+ return mManager.createEffectHal(pEffectUuid, sessionId, deviceId, effect);
+ }
+ status_t allocateHalBuffer(size_t size __unused,
+ sp<EffectBufferHalInterface>* buffer __unused) override { return NO_ERROR; }
+ bool updateOrphanEffectChains(const sp<EffectBase>& effect __unused) override { return false; }
+
+ audio_io_handle_t io() const override { return AUDIO_IO_HANDLE_NONE; }
+ bool isOutput() const override { return false; }
+ bool isOffload() const override { return false; }
+ bool isOffloadOrDirect() const override { return false; }
+ bool isOffloadOrMmap() const override { return false; }
+
+ uint32_t sampleRate() const override { return 0; }
+ audio_channel_mask_t channelMask() const override { return AUDIO_CHANNEL_NONE; }
+ uint32_t channelCount() const override { return 0; }
+ size_t frameCount() const override { return 0; }
+ uint32_t latency() const override { return 0; }
+
+ status_t addEffectToHal(sp<EffectHalInterface> effect __unused) override {
+ return NO_ERROR;
+ }
+ status_t removeEffectFromHal(sp<EffectHalInterface> effect __unused) override {
+ return NO_ERROR;
+ }
+
+ bool disconnectEffectHandle(EffectHandle *handle, bool unpinIfLast) override;
+ void setVolumeForOutput(float left __unused, float right __unused) const override {}
+
+ // check if effects should be suspended or restored when a given effect is enable or disabled
+ void checkSuspendOnEffectEnabled(const sp<EffectBase>& effect __unused,
+ bool enabled __unused, bool threadLocked __unused) override {}
+ void resetVolume() override {}
+ uint32_t strategy() const override { return 0; }
+ int32_t activeTrackCnt() const override { return 0; }
+ void onEffectEnable(const sp<EffectBase>& effect __unused) override {}
+ void onEffectDisable(const sp<EffectBase>& effect __unused) override {}
+
+ wp<EffectChain> chain() const override { return nullptr; }
+
+ int newEffectId() { return mManager.audioFlinger().nextUniqueId(AUDIO_UNIQUE_ID_USE_EFFECT); }
+
+ status_t addEffectToHal(audio_port_handle_t deviceId,
+ audio_module_handle_t hwModuleId, sp<EffectHalInterface> effect) {
+ return mManager.addEffectToHal(deviceId, hwModuleId, effect);
+ }
+ status_t removeEffectFromHal(audio_port_handle_t deviceId,
+ audio_module_handle_t hwModuleId, sp<EffectHalInterface> effect) {
+ return mManager.removeEffectFromHal(deviceId, hwModuleId, effect);
+ }
+private:
+ DeviceEffectManager& mManager;
+};
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index f27c6ad..2f3724f 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -58,81 +58,115 @@
namespace android {
// ----------------------------------------------------------------------------
-// EffectModule implementation
+// EffectBase implementation
// ----------------------------------------------------------------------------
#undef LOG_TAG
-#define LOG_TAG "AudioFlinger::EffectModule"
+#define LOG_TAG "AudioFlinger::EffectBase"
-AudioFlinger::EffectModule::EffectModule(ThreadBase *thread,
- const wp<AudioFlinger::EffectChain>& chain,
+AudioFlinger::EffectBase::EffectBase(const sp<AudioFlinger::EffectCallbackInterface>& callback,
effect_descriptor_t *desc,
int id,
audio_session_t sessionId,
bool pinned)
: mPinned(pinned),
- mThread(thread), mChain(chain), mId(id), mSessionId(sessionId),
- mDescriptor(*desc),
- // clear mConfig to ensure consistent initial value of buffer framecount
- // in case buffers are associated by setInBuffer() or setOutBuffer()
- // prior to configure().
- mConfig{{}, {}},
- mStatus(NO_INIT), mState(IDLE),
- mMaxDisableWaitCnt(1), // set by configure(), should be >= 1
- mDisableWaitCnt(0), // set by process() and updateState()
- mSuspended(false),
- mOffloaded(false),
- mAudioFlinger(thread->mAudioFlinger)
-#ifdef FLOAT_EFFECT_CHAIN
- , mSupportsFloat(false)
-#endif
+ mCallback(callback), mId(id), mSessionId(sessionId),
+ mDescriptor(*desc)
{
- ALOGV("Constructor %p pinned %d", this, pinned);
- int lStatus;
+}
- // create effect engine from effect factory
- mStatus = -ENODEV;
- sp<AudioFlinger> audioFlinger = mAudioFlinger.promote();
- if (audioFlinger != 0) {
- sp<EffectsFactoryHalInterface> effectsFactory = audioFlinger->getEffectsFactory();
- if (effectsFactory != 0) {
- mStatus = effectsFactory->createEffect(
- &desc->uuid, sessionId, thread->id(), AUDIO_PORT_HANDLE_NONE, &mEffectInterface);
+// must be called with EffectModule::mLock held
+status_t AudioFlinger::EffectBase::setEnabled_l(bool enabled)
+{
+
+ ALOGV("setEnabled %p enabled %d", this, enabled);
+
+ if (enabled != isEnabled()) {
+ switch (mState) {
+ // going from disabled to enabled
+ case IDLE:
+ mState = STARTING;
+ break;
+ case STOPPED:
+ mState = RESTART;
+ break;
+ case STOPPING:
+ mState = ACTIVE;
+ break;
+
+ // going from enabled to disabled
+ case RESTART:
+ mState = STOPPED;
+ break;
+ case STARTING:
+ mState = IDLE;
+ break;
+ case ACTIVE:
+ mState = STOPPING;
+ break;
+ case DESTROYED:
+ return NO_ERROR; // simply ignore as we are being destroyed
+ }
+ for (size_t i = 1; i < mHandles.size(); i++) {
+ EffectHandle *h = mHandles[i];
+ if (h != NULL && !h->disconnected()) {
+ h->setEnabled(enabled);
+ }
}
}
-
- if (mStatus != NO_ERROR) {
- return;
- }
- lStatus = init();
- if (lStatus < 0) {
- mStatus = lStatus;
- goto Error;
- }
-
- setOffloaded(thread->type() == ThreadBase::OFFLOAD, thread->id());
- ALOGV("Constructor success name %s, Interface %p", mDescriptor.name, mEffectInterface.get());
-
- return;
-Error:
- mEffectInterface.clear();
- ALOGV("Constructor Error %d", mStatus);
+ return NO_ERROR;
}
-AudioFlinger::EffectModule::~EffectModule()
+status_t AudioFlinger::EffectBase::setEnabled(bool enabled, bool fromHandle)
{
- ALOGV("Destructor %p", this);
- if (mEffectInterface != 0) {
- char uuidStr[64];
- AudioEffect::guidToString(&mDescriptor.uuid, uuidStr, sizeof(uuidStr));
- ALOGW("EffectModule %p destructor called with unreleased interface, effect %s",
- this, uuidStr);
- release_l();
+ status_t status;
+ {
+ Mutex::Autolock _l(mLock);
+ status = setEnabled_l(enabled);
}
-
+ if (fromHandle) {
+ if (enabled) {
+ if (status != NO_ERROR) {
+ mCallback->checkSuspendOnEffectEnabled(this, false, false /*threadLocked*/);
+ } else {
+ mCallback->onEffectEnable(this);
+ }
+ } else {
+ mCallback->onEffectDisable(this);
+ }
+ }
+ return status;
}
-status_t AudioFlinger::EffectModule::addHandle(EffectHandle *handle)
+bool AudioFlinger::EffectBase::isEnabled() const
+{
+ switch (mState) {
+ case RESTART:
+ case STARTING:
+ case ACTIVE:
+ return true;
+ case IDLE:
+ case STOPPING:
+ case STOPPED:
+ case DESTROYED:
+ default:
+ return false;
+ }
+}
+
+void AudioFlinger::EffectBase::setSuspended(bool suspended)
+{
+ Mutex::Autolock _l(mLock);
+ mSuspended = suspended;
+}
+
+bool AudioFlinger::EffectBase::suspended() const
+{
+ Mutex::Autolock _l(mLock);
+ return mSuspended;
+}
+
+status_t AudioFlinger::EffectBase::addHandle(EffectHandle *handle)
{
status_t status;
@@ -171,7 +205,7 @@
return status;
}
-status_t AudioFlinger::EffectModule::updatePolicyState()
+status_t AudioFlinger::EffectBase::updatePolicyState()
{
status_t status = NO_ERROR;
bool doRegister = false;
@@ -188,14 +222,8 @@
doRegister = true;
mPolicyRegistered = mHandles.size() > 0;
if (mPolicyRegistered) {
- sp <EffectChain> chain = mChain.promote();
- sp <ThreadBase> thread = mThread.promote();
-
- if (thread == nullptr || chain == nullptr) {
- return INVALID_OPERATION;
- }
- io = thread->id();
- strategy = chain->strategy();
+ io = mCallback->io();
+ strategy = mCallback->strategy();
}
}
// enable effect when registered according to enable state requested by controlling handle
@@ -233,13 +261,13 @@
}
-ssize_t AudioFlinger::EffectModule::removeHandle(EffectHandle *handle)
+ssize_t AudioFlinger::EffectBase::removeHandle(EffectHandle *handle)
{
Mutex::Autolock _l(mLock);
return removeHandle_l(handle);
}
-ssize_t AudioFlinger::EffectModule::removeHandle_l(EffectHandle *handle)
+ssize_t AudioFlinger::EffectBase::removeHandle_l(EffectHandle *handle)
{
size_t size = mHandles.size();
size_t i;
@@ -263,19 +291,15 @@
}
}
- // Prevent calls to process() and other functions on effect interface from now on.
- // The effect engine will be released by the destructor when the last strong reference on
- // this object is released which can happen after next process is called.
if (mHandles.size() == 0 && !mPinned) {
mState = DESTROYED;
- mEffectInterface->close();
}
return mHandles.size();
}
// must be called with EffectModule::mLock held
-AudioFlinger::EffectHandle *AudioFlinger::EffectModule::controlHandle_l()
+AudioFlinger::EffectHandle *AudioFlinger::EffectBase::controlHandle_l()
{
// the first valid handle in the list has control over the module
for (size_t i = 0; i < mHandles.size(); i++) {
@@ -289,22 +313,271 @@
}
// unsafe method called when the effect parent thread has been destroyed
-ssize_t AudioFlinger::EffectModule::disconnectHandle(EffectHandle *handle, bool unpinIfLast)
+ssize_t AudioFlinger::EffectBase::disconnectHandle(EffectHandle *handle, bool unpinIfLast)
{
ALOGV("disconnect() %p handle %p", this, handle);
+ if (mCallback->disconnectEffectHandle(handle, unpinIfLast)) {
+ return mHandles.size();
+ }
+
Mutex::Autolock _l(mLock);
ssize_t numHandles = removeHandle_l(handle);
if ((numHandles == 0) && (!mPinned || unpinIfLast)) {
- sp<AudioFlinger> af = mAudioFlinger.promote();
- if (af != 0) {
- mLock.unlock();
- af->updateOrphanEffectChains(this);
- mLock.lock();
- }
+ mLock.unlock();
+ mCallback->updateOrphanEffectChains(this);
+ mLock.lock();
}
return numHandles;
}
+bool AudioFlinger::EffectBase::purgeHandles()
+{
+ bool enabled = false;
+ Mutex::Autolock _l(mLock);
+ EffectHandle *handle = controlHandle_l();
+ if (handle != NULL) {
+ enabled = handle->enabled();
+ }
+ mHandles.clear();
+ return enabled;
+}
+
+void AudioFlinger::EffectBase::checkSuspendOnEffectEnabled(bool enabled, bool threadLocked) {
+ mCallback->checkSuspendOnEffectEnabled(this, enabled, threadLocked);
+}
+
+static String8 effectFlagsToString(uint32_t flags) {
+ String8 s;
+
+ s.append("conn. mode: ");
+ switch (flags & EFFECT_FLAG_TYPE_MASK) {
+ case EFFECT_FLAG_TYPE_INSERT: s.append("insert"); break;
+ case EFFECT_FLAG_TYPE_AUXILIARY: s.append("auxiliary"); break;
+ case EFFECT_FLAG_TYPE_REPLACE: s.append("replace"); break;
+ case EFFECT_FLAG_TYPE_PRE_PROC: s.append("preproc"); break;
+ case EFFECT_FLAG_TYPE_POST_PROC: s.append("postproc"); break;
+ default: s.append("unknown/reserved"); break;
+ }
+ s.append(", ");
+
+ s.append("insert pref: ");
+ switch (flags & EFFECT_FLAG_INSERT_MASK) {
+ case EFFECT_FLAG_INSERT_ANY: s.append("any"); break;
+ case EFFECT_FLAG_INSERT_FIRST: s.append("first"); break;
+ case EFFECT_FLAG_INSERT_LAST: s.append("last"); break;
+ case EFFECT_FLAG_INSERT_EXCLUSIVE: s.append("exclusive"); break;
+ default: s.append("unknown/reserved"); break;
+ }
+ s.append(", ");
+
+ s.append("volume mgmt: ");
+ switch (flags & EFFECT_FLAG_VOLUME_MASK) {
+ case EFFECT_FLAG_VOLUME_NONE: s.append("none"); break;
+ case EFFECT_FLAG_VOLUME_CTRL: s.append("implements control"); break;
+ case EFFECT_FLAG_VOLUME_IND: s.append("requires indication"); break;
+ case EFFECT_FLAG_VOLUME_MONITOR: s.append("monitors volume"); break;
+ default: s.append("unknown/reserved"); break;
+ }
+ s.append(", ");
+
+ uint32_t devind = flags & EFFECT_FLAG_DEVICE_MASK;
+ if (devind) {
+ s.append("device indication: ");
+ switch (devind) {
+ case EFFECT_FLAG_DEVICE_IND: s.append("requires updates"); break;
+ default: s.append("unknown/reserved"); break;
+ }
+ s.append(", ");
+ }
+
+ s.append("input mode: ");
+ switch (flags & EFFECT_FLAG_INPUT_MASK) {
+ case EFFECT_FLAG_INPUT_DIRECT: s.append("direct"); break;
+ case EFFECT_FLAG_INPUT_PROVIDER: s.append("provider"); break;
+ case EFFECT_FLAG_INPUT_BOTH: s.append("direct+provider"); break;
+ default: s.append("not set"); break;
+ }
+ s.append(", ");
+
+ s.append("output mode: ");
+ switch (flags & EFFECT_FLAG_OUTPUT_MASK) {
+ case EFFECT_FLAG_OUTPUT_DIRECT: s.append("direct"); break;
+ case EFFECT_FLAG_OUTPUT_PROVIDER: s.append("provider"); break;
+ case EFFECT_FLAG_OUTPUT_BOTH: s.append("direct+provider"); break;
+ default: s.append("not set"); break;
+ }
+ s.append(", ");
+
+ uint32_t accel = flags & EFFECT_FLAG_HW_ACC_MASK;
+ if (accel) {
+ s.append("hardware acceleration: ");
+ switch (accel) {
+ case EFFECT_FLAG_HW_ACC_SIMPLE: s.append("non-tunneled"); break;
+ case EFFECT_FLAG_HW_ACC_TUNNEL: s.append("tunneled"); break;
+ default: s.append("unknown/reserved"); break;
+ }
+ s.append(", ");
+ }
+
+ uint32_t modeind = flags & EFFECT_FLAG_AUDIO_MODE_MASK;
+ if (modeind) {
+ s.append("mode indication: ");
+ switch (modeind) {
+ case EFFECT_FLAG_AUDIO_MODE_IND: s.append("required"); break;
+ default: s.append("unknown/reserved"); break;
+ }
+ s.append(", ");
+ }
+
+ uint32_t srcind = flags & EFFECT_FLAG_AUDIO_SOURCE_MASK;
+ if (srcind) {
+ s.append("source indication: ");
+ switch (srcind) {
+ case EFFECT_FLAG_AUDIO_SOURCE_IND: s.append("required"); break;
+ default: s.append("unknown/reserved"); break;
+ }
+ s.append(", ");
+ }
+
+ if (flags & EFFECT_FLAG_OFFLOAD_MASK) {
+ s.append("offloadable, ");
+ }
+
+ int len = s.length();
+ if (s.length() > 2) {
+ (void) s.lockBuffer(len);
+ s.unlockBuffer(len - 2);
+ }
+ return s;
+}
+
+void AudioFlinger::EffectBase::dump(int fd, const Vector<String16>& args __unused)
+{
+ String8 result;
+
+ result.appendFormat("\tEffect ID %d:\n", mId);
+
+ bool locked = AudioFlinger::dumpTryLock(mLock);
+ // failed to lock - AudioFlinger is probably deadlocked
+ if (!locked) {
+ result.append("\t\tCould not lock Fx mutex:\n");
+ }
+
+ result.append("\t\tSession State Registered Enabled Suspended:\n");
+ result.appendFormat("\t\t%05d %03d %s %s %s\n",
+ mSessionId, mState, mPolicyRegistered ? "y" : "n",
+ mPolicyEnabled ? "y" : "n", mSuspended ? "y" : "n");
+
+ result.append("\t\tDescriptor:\n");
+ char uuidStr[64];
+ AudioEffect::guidToString(&mDescriptor.uuid, uuidStr, sizeof(uuidStr));
+ result.appendFormat("\t\t- UUID: %s\n", uuidStr);
+ AudioEffect::guidToString(&mDescriptor.type, uuidStr, sizeof(uuidStr));
+ result.appendFormat("\t\t- TYPE: %s\n", uuidStr);
+ result.appendFormat("\t\t- apiVersion: %08X\n\t\t- flags: %08X (%s)\n",
+ mDescriptor.apiVersion,
+ mDescriptor.flags,
+ effectFlagsToString(mDescriptor.flags).string());
+ result.appendFormat("\t\t- name: %s\n",
+ mDescriptor.name);
+
+ result.appendFormat("\t\t- implementor: %s\n",
+ mDescriptor.implementor);
+
+ result.appendFormat("\t\t%zu Clients:\n", mHandles.size());
+ result.append("\t\t\t Pid Priority Ctrl Locked client server\n");
+ char buffer[256];
+ for (size_t i = 0; i < mHandles.size(); ++i) {
+ EffectHandle *handle = mHandles[i];
+ if (handle != NULL && !handle->disconnected()) {
+ handle->dumpToBuffer(buffer, sizeof(buffer));
+ result.append(buffer);
+ }
+ }
+ if (locked) {
+ mLock.unlock();
+ }
+
+ write(fd, result.string(), result.length());
+}
+
+// ----------------------------------------------------------------------------
+// EffectModule implementation
+// ----------------------------------------------------------------------------
+
+#undef LOG_TAG
+#define LOG_TAG "AudioFlinger::EffectModule"
+
+AudioFlinger::EffectModule::EffectModule(const sp<AudioFlinger::EffectCallbackInterface>& callback,
+ effect_descriptor_t *desc,
+ int id,
+ audio_session_t sessionId,
+ bool pinned,
+ audio_port_handle_t deviceId)
+ : EffectBase(callback, desc, id, sessionId, pinned),
+ // clear mConfig to ensure consistent initial value of buffer framecount
+ // in case buffers are associated by setInBuffer() or setOutBuffer()
+ // prior to configure().
+ mConfig{{}, {}},
+ mStatus(NO_INIT),
+ mMaxDisableWaitCnt(1), // set by configure(), should be >= 1
+ mDisableWaitCnt(0), // set by process() and updateState()
+ mOffloaded(false)
+#ifdef FLOAT_EFFECT_CHAIN
+ , mSupportsFloat(false)
+#endif
+{
+ ALOGV("Constructor %p pinned %d", this, pinned);
+ int lStatus;
+
+ // create effect engine from effect factory
+ mStatus = callback->createEffectHal(
+ &desc->uuid, sessionId, deviceId, &mEffectInterface);
+ if (mStatus != NO_ERROR) {
+ return;
+ }
+ lStatus = init();
+ if (lStatus < 0) {
+ mStatus = lStatus;
+ goto Error;
+ }
+
+ setOffloaded(callback->isOffload(), callback->io());
+ ALOGV("Constructor success name %s, Interface %p", mDescriptor.name, mEffectInterface.get());
+
+ return;
+Error:
+ mEffectInterface.clear();
+ ALOGV("Constructor Error %d", mStatus);
+}
+
+AudioFlinger::EffectModule::~EffectModule()
+{
+ ALOGV("Destructor %p", this);
+ if (mEffectInterface != 0) {
+ char uuidStr[64];
+ AudioEffect::guidToString(&mDescriptor.uuid, uuidStr, sizeof(uuidStr));
+ ALOGW("EffectModule %p destructor called with unreleased interface, effect %s",
+ this, uuidStr);
+ release_l();
+ }
+
+}
+
+ssize_t AudioFlinger::EffectModule::removeHandle_l(EffectHandle *handle)
+{
+ ssize_t status = EffectBase::removeHandle_l(handle);
+
+ // Prevent calls to process() and other functions on effect interface from now on.
+ // The effect engine will be released by the destructor when the last strong reference on
+ // this object is released which can happen after next process is called.
+ if (status == 0 && !mPinned) {
+ mEffectInterface->close();
+ }
+
+ return status;
+}
+
bool AudioFlinger::EffectModule::updateState() {
Mutex::Autolock _l(mLock);
@@ -464,7 +737,7 @@
}
if (!mSupportsFloat) { // convert input to int16_t as effect doesn't support float.
if (!auxType) {
- if (mInConversionBuffer.get() == nullptr) {
+ if (mInConversionBuffer == nullptr) {
ALOGW("%s: mInConversionBuffer is null, bypassing", __func__);
goto data_bypass;
}
@@ -475,7 +748,7 @@
inBuffer = mInConversionBuffer;
}
if (mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
- if (mOutConversionBuffer.get() == nullptr) {
+ if (mOutConversionBuffer == nullptr) {
ALOGW("%s: mOutConversionBuffer is null, bypassing", __func__);
goto data_bypass;
}
@@ -542,8 +815,7 @@
mConfig.inputCfg.buffer.raw != mConfig.outputCfg.buffer.raw) {
// If an insert effect is idle and input buffer is different from output buffer,
// accumulate input onto output
- sp<EffectChain> chain = mChain.promote();
- if (chain.get() != nullptr && chain->activeTrackCnt() != 0) {
+ if (mCallback->activeTrackCnt() != 0) {
// similar handling with data_bypass above.
if (mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
accumulateInputToOutput();
@@ -566,7 +838,6 @@
{
ALOGVV("configure() started");
status_t status;
- sp<ThreadBase> thread;
uint32_t size;
audio_channel_mask_t channelMask;
@@ -575,17 +846,11 @@
goto exit;
}
- thread = mThread.promote();
- if (thread == 0) {
- status = DEAD_OBJECT;
- goto exit;
- }
-
// TODO: handle configuration of effects replacing track process
// TODO: handle configuration of input (record) SW effects above the HAL,
// similar to output EFFECT_FLAG_TYPE_INSERT/REPLACE,
// in which case input channel masks should be used here.
- channelMask = thread->channelMask();
+ channelMask = mCallback->channelMask();
mConfig.inputCfg.channels = channelMask;
mConfig.outputCfg.channels = channelMask;
@@ -622,11 +887,11 @@
mConfig.outputCfg.format = EFFECT_BUFFER_FORMAT;
// Don't use sample rate for thread if effect isn't offloadable.
- if ((thread->type() == ThreadBase::OFFLOAD) && !isOffloaded()) {
+ if (mCallback->isOffloadOrDirect() && !isOffloaded()) {
mConfig.inputCfg.samplingRate = DEFAULT_OUTPUT_SAMPLE_RATE;
ALOGV("Overriding effect input as 48kHz");
} else {
- mConfig.inputCfg.samplingRate = thread->sampleRate();
+ mConfig.inputCfg.samplingRate = mCallback->sampleRate();
}
mConfig.outputCfg.samplingRate = mConfig.inputCfg.samplingRate;
mConfig.inputCfg.bufferProvider.cookie = NULL;
@@ -652,11 +917,12 @@
}
mConfig.inputCfg.mask = EFFECT_CONFIG_ALL;
mConfig.outputCfg.mask = EFFECT_CONFIG_ALL;
- mConfig.inputCfg.buffer.frameCount = thread->frameCount();
+ mConfig.inputCfg.buffer.frameCount = mCallback->frameCount();
mConfig.outputCfg.buffer.frameCount = mConfig.inputCfg.buffer.frameCount;
- ALOGV("configure() %p thread %p buffer %p framecount %zu",
- this, thread.get(), mConfig.inputCfg.buffer.raw, mConfig.inputCfg.buffer.frameCount);
+ ALOGV("configure() %p chain %p buffer %p framecount %zu",
+ this, mCallback->chain().promote().get(),
+ mConfig.inputCfg.buffer.raw, mConfig.inputCfg.buffer.frameCount);
status_t cmdStatus;
size = sizeof(int);
@@ -671,7 +937,7 @@
#ifdef MULTICHANNEL_EFFECT_CHAIN
if (status != NO_ERROR &&
- thread->isOutput() &&
+ mCallback->isOutput() &&
(mConfig.inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO
|| mConfig.outputCfg.channels != AUDIO_CHANNEL_OUT_STEREO)) {
// Older effects may require exact STEREO position mask.
@@ -738,11 +1004,7 @@
size = sizeof(int);
*(int32_t *)p->data = VISUALIZER_PARAM_LATENCY;
- uint32_t latency = 0;
- PlaybackThread *pbt = thread->mAudioFlinger->checkPlaybackThread_l(thread->mId);
- if (pbt != NULL) {
- latency = pbt->latency_l();
- }
+ uint32_t latency = mCallback->latency();
*((int32_t *)p->data + 1)= latency;
mEffectInterface->command(EFFECT_CMD_SET_PARAM,
@@ -789,31 +1051,20 @@
{
if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC ||
(mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_POST_PROC) {
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0) {
- sp<StreamHalInterface> stream = thread->stream();
- if (stream != 0) {
- status_t result = stream->addEffect(mEffectInterface);
- ALOGE_IF(result != OK, "Error when adding effect: %d", result);
- }
- }
+ (void)mCallback->addEffectToHal(mEffectInterface);
}
}
// start() must be called with PlaybackThread::mLock or EffectChain::mLock held
status_t AudioFlinger::EffectModule::start()
{
- sp<EffectChain> chain;
status_t status;
{
Mutex::Autolock _l(mLock);
status = start_l();
- if (status == NO_ERROR) {
- chain = mChain.promote();
- }
}
- if (chain != 0) {
- chain->resetVolume_l();
+ if (status == NO_ERROR) {
+ mCallback->resetVolume();
}
return status;
}
@@ -860,11 +1111,10 @@
uint32_t size = sizeof(status_t);
if (isVolumeControl() && isOffloadedOrDirect()) {
- sp<EffectChain>chain = mChain.promote();
// We have the EffectChain and EffectModule lock, permit a reentrant call to setVolume:
// resetVolume_l --> setVolume_l --> EffectModule::setVolume
mSetVolumeReentrantTid = gettid();
- chain->resetVolume_l();
+ mCallback->resetVolume();
mSetVolumeReentrantTid = INVALID_PID;
}
@@ -877,7 +1127,7 @@
status = cmdStatus;
}
if (status == NO_ERROR) {
- status = remove_effect_from_hal_l();
+ status = removeEffectFromHal_l();
}
return status;
}
@@ -886,25 +1136,18 @@
void AudioFlinger::EffectModule::release_l()
{
if (mEffectInterface != 0) {
- remove_effect_from_hal_l();
+ removeEffectFromHal_l();
// release effect engine
mEffectInterface->close();
mEffectInterface.clear();
}
}
-status_t AudioFlinger::EffectModule::remove_effect_from_hal_l()
+status_t AudioFlinger::EffectModule::removeEffectFromHal_l()
{
if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC ||
(mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_POST_PROC) {
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0) {
- sp<StreamHalInterface> stream = thread->stream();
- if (stream != 0) {
- status_t result = stream->removeEffect(mEffectInterface);
- ALOGE_IF(result != OK, "Error when removing effect: %d", result);
- }
- }
+ mCallback->removeEffectFromHal(mEffectInterface);
}
return NO_ERROR;
}
@@ -994,70 +1237,6 @@
return status;
}
-status_t AudioFlinger::EffectModule::setEnabled(bool enabled)
-{
- Mutex::Autolock _l(mLock);
- return setEnabled_l(enabled);
-}
-
-// must be called with EffectModule::mLock held
-status_t AudioFlinger::EffectModule::setEnabled_l(bool enabled)
-{
-
- ALOGV("setEnabled %p enabled %d", this, enabled);
-
- if (enabled != isEnabled()) {
- switch (mState) {
- // going from disabled to enabled
- case IDLE:
- mState = STARTING;
- break;
- case STOPPED:
- mState = RESTART;
- break;
- case STOPPING:
- mState = ACTIVE;
- break;
-
- // going from enabled to disabled
- case RESTART:
- mState = STOPPED;
- break;
- case STARTING:
- mState = IDLE;
- break;
- case ACTIVE:
- mState = STOPPING;
- break;
- case DESTROYED:
- return NO_ERROR; // simply ignore as we are being destroyed
- }
- for (size_t i = 1; i < mHandles.size(); i++) {
- EffectHandle *h = mHandles[i];
- if (h != NULL && !h->disconnected()) {
- h->setEnabled(enabled);
- }
- }
- }
- return NO_ERROR;
-}
-
-bool AudioFlinger::EffectModule::isEnabled() const
-{
- switch (mState) {
- case RESTART:
- case STARTING:
- case ACTIVE:
- return true;
- case IDLE:
- case STOPPING:
- case STOPPED:
- case DESTROYED:
- default:
- return false;
- }
-}
-
bool AudioFlinger::EffectModule::isProcessEnabled() const
{
if (mStatus != NO_ERROR) {
@@ -1080,7 +1259,7 @@
bool AudioFlinger::EffectModule::isOffloadedOrDirect() const
{
- return (mThreadType == ThreadBase::OFFLOAD || mThreadType == ThreadBase::DIRECT);
+ return mCallback->isOffloadOrDirect();
}
bool AudioFlinger::EffectModule::isVolumeControlEnabled() const
@@ -1110,7 +1289,7 @@
const uint32_t inChannelCount =
audio_channel_count_from_out_mask(mConfig.inputCfg.channels);
const bool formatMismatch = !mSupportsFloat || mInChannelCountRequested != inChannelCount;
- if (!auxType && formatMismatch && mInBuffer.get() != nullptr) {
+ if (!auxType && formatMismatch && mInBuffer != nullptr) {
// we need to translate - create hidl shared buffer and intercept
const size_t inFrameCount = mConfig.inputCfg.buffer.frameCount;
// Use FCC_2 in case mInChannelCountRequested is mono and the effect is stereo.
@@ -1120,15 +1299,13 @@
ALOGV("%s: setInBuffer updating for inChannels:%d inFrameCount:%zu total size:%zu",
__func__, inChannels, inFrameCount, size);
- if (size > 0 && (mInConversionBuffer.get() == nullptr
+ if (size > 0 && (mInConversionBuffer == nullptr
|| size > mInConversionBuffer->getSize())) {
mInConversionBuffer.clear();
ALOGV("%s: allocating mInConversionBuffer %zu", __func__, size);
- sp<AudioFlinger> audioFlinger = mAudioFlinger.promote();
- LOG_ALWAYS_FATAL_IF(audioFlinger == nullptr, "EM could not retrieved audioFlinger");
- (void)audioFlinger->mEffectsFactoryHal->allocateBuffer(size, &mInConversionBuffer);
+ (void)mCallback->allocateHalBuffer(size, &mInConversionBuffer);
}
- if (mInConversionBuffer.get() != nullptr) {
+ if (mInConversionBuffer != nullptr) {
mInConversionBuffer->setFrameCount(inFrameCount);
mEffectInterface->setInBuffer(mInConversionBuffer);
} else if (size > 0) {
@@ -1157,7 +1334,7 @@
const uint32_t outChannelCount =
audio_channel_count_from_out_mask(mConfig.outputCfg.channels);
const bool formatMismatch = !mSupportsFloat || mOutChannelCountRequested != outChannelCount;
- if (formatMismatch && mOutBuffer.get() != nullptr) {
+ if (formatMismatch && mOutBuffer != nullptr) {
const size_t outFrameCount = mConfig.outputCfg.buffer.frameCount;
// Use FCC_2 in case mOutChannelCountRequested is mono and the effect is stereo.
const uint32_t outChannels = std::max((uint32_t)FCC_2, mOutChannelCountRequested);
@@ -1166,15 +1343,13 @@
ALOGV("%s: setOutBuffer updating for outChannels:%d outFrameCount:%zu total size:%zu",
__func__, outChannels, outFrameCount, size);
- if (size > 0 && (mOutConversionBuffer.get() == nullptr
+ if (size > 0 && (mOutConversionBuffer == nullptr
|| size > mOutConversionBuffer->getSize())) {
mOutConversionBuffer.clear();
ALOGV("%s: allocating mOutConversionBuffer %zu", __func__, size);
- sp<AudioFlinger> audioFlinger = mAudioFlinger.promote();
- LOG_ALWAYS_FATAL_IF(audioFlinger == nullptr, "EM could not retrieved audioFlinger");
- (void)audioFlinger->mEffectsFactoryHal->allocateBuffer(size, &mOutConversionBuffer);
+ (void)mCallback->allocateHalBuffer(size, &mOutConversionBuffer);
}
- if (mOutConversionBuffer.get() != nullptr) {
+ if (mOutConversionBuffer != nullptr) {
mOutConversionBuffer->setFrameCount(outFrameCount);
mEffectInterface->setOutBuffer(mOutConversionBuffer);
} else if (size > 0) {
@@ -1220,14 +1395,10 @@
void AudioFlinger::EffectChain::setVolumeForOutput_l(uint32_t left, uint32_t right)
{
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0 &&
- (thread->type() == ThreadBase::OFFLOAD || thread->type() == ThreadBase::DIRECT) &&
- !isNonOffloadableEnabled_l()) {
- PlaybackThread *t = (PlaybackThread *)thread.get();
+ if (mEffectCallback->isOffloadOrDirect() && !isNonOffloadableEnabled_l()) {
float vol_l = (float)left / (1 << 24);
float vol_r = (float)right / (1 << 24);
- t->setVolumeForOutput_l(vol_l, vol_r);
+ mEffectCallback->setVolumeForOutput(vol_l, vol_r);
}
}
@@ -1307,30 +1478,6 @@
return status;
}
-void AudioFlinger::EffectModule::setSuspended(bool suspended)
-{
- Mutex::Autolock _l(mLock);
- mSuspended = suspended;
-}
-
-bool AudioFlinger::EffectModule::suspended() const
-{
- Mutex::Autolock _l(mLock);
- return mSuspended;
-}
-
-bool AudioFlinger::EffectModule::purgeHandles()
-{
- bool enabled = false;
- Mutex::Autolock _l(mLock);
- EffectHandle *handle = controlHandle_l();
- if (handle != NULL) {
- enabled = handle->enabled();
- }
- mHandles.clear();
- return enabled;
-}
-
status_t AudioFlinger::EffectModule::setOffloaded(bool offloaded, audio_io_handle_t io)
{
Mutex::Autolock _l(mLock);
@@ -1370,115 +1517,10 @@
return mOffloaded;
}
-String8 effectFlagsToString(uint32_t flags) {
- String8 s;
-
- s.append("conn. mode: ");
- switch (flags & EFFECT_FLAG_TYPE_MASK) {
- case EFFECT_FLAG_TYPE_INSERT: s.append("insert"); break;
- case EFFECT_FLAG_TYPE_AUXILIARY: s.append("auxiliary"); break;
- case EFFECT_FLAG_TYPE_REPLACE: s.append("replace"); break;
- case EFFECT_FLAG_TYPE_PRE_PROC: s.append("preproc"); break;
- case EFFECT_FLAG_TYPE_POST_PROC: s.append("postproc"); break;
- default: s.append("unknown/reserved"); break;
- }
- s.append(", ");
-
- s.append("insert pref: ");
- switch (flags & EFFECT_FLAG_INSERT_MASK) {
- case EFFECT_FLAG_INSERT_ANY: s.append("any"); break;
- case EFFECT_FLAG_INSERT_FIRST: s.append("first"); break;
- case EFFECT_FLAG_INSERT_LAST: s.append("last"); break;
- case EFFECT_FLAG_INSERT_EXCLUSIVE: s.append("exclusive"); break;
- default: s.append("unknown/reserved"); break;
- }
- s.append(", ");
-
- s.append("volume mgmt: ");
- switch (flags & EFFECT_FLAG_VOLUME_MASK) {
- case EFFECT_FLAG_VOLUME_NONE: s.append("none"); break;
- case EFFECT_FLAG_VOLUME_CTRL: s.append("implements control"); break;
- case EFFECT_FLAG_VOLUME_IND: s.append("requires indication"); break;
- case EFFECT_FLAG_VOLUME_MONITOR: s.append("monitors volume"); break;
- default: s.append("unknown/reserved"); break;
- }
- s.append(", ");
-
- uint32_t devind = flags & EFFECT_FLAG_DEVICE_MASK;
- if (devind) {
- s.append("device indication: ");
- switch (devind) {
- case EFFECT_FLAG_DEVICE_IND: s.append("requires updates"); break;
- default: s.append("unknown/reserved"); break;
- }
- s.append(", ");
- }
-
- s.append("input mode: ");
- switch (flags & EFFECT_FLAG_INPUT_MASK) {
- case EFFECT_FLAG_INPUT_DIRECT: s.append("direct"); break;
- case EFFECT_FLAG_INPUT_PROVIDER: s.append("provider"); break;
- case EFFECT_FLAG_INPUT_BOTH: s.append("direct+provider"); break;
- default: s.append("not set"); break;
- }
- s.append(", ");
-
- s.append("output mode: ");
- switch (flags & EFFECT_FLAG_OUTPUT_MASK) {
- case EFFECT_FLAG_OUTPUT_DIRECT: s.append("direct"); break;
- case EFFECT_FLAG_OUTPUT_PROVIDER: s.append("provider"); break;
- case EFFECT_FLAG_OUTPUT_BOTH: s.append("direct+provider"); break;
- default: s.append("not set"); break;
- }
- s.append(", ");
-
- uint32_t accel = flags & EFFECT_FLAG_HW_ACC_MASK;
- if (accel) {
- s.append("hardware acceleration: ");
- switch (accel) {
- case EFFECT_FLAG_HW_ACC_SIMPLE: s.append("non-tunneled"); break;
- case EFFECT_FLAG_HW_ACC_TUNNEL: s.append("tunneled"); break;
- default: s.append("unknown/reserved"); break;
- }
- s.append(", ");
- }
-
- uint32_t modeind = flags & EFFECT_FLAG_AUDIO_MODE_MASK;
- if (modeind) {
- s.append("mode indication: ");
- switch (modeind) {
- case EFFECT_FLAG_AUDIO_MODE_IND: s.append("required"); break;
- default: s.append("unknown/reserved"); break;
- }
- s.append(", ");
- }
-
- uint32_t srcind = flags & EFFECT_FLAG_AUDIO_SOURCE_MASK;
- if (srcind) {
- s.append("source indication: ");
- switch (srcind) {
- case EFFECT_FLAG_AUDIO_SOURCE_IND: s.append("required"); break;
- default: s.append("unknown/reserved"); break;
- }
- s.append(", ");
- }
-
- if (flags & EFFECT_FLAG_OFFLOAD_MASK) {
- s.append("offloadable, ");
- }
-
- int len = s.length();
- if (s.length() > 2) {
- (void) s.lockBuffer(len);
- s.unlockBuffer(len - 2);
- }
- return s;
-}
-
static std::string dumpInOutBuffer(bool isInput, const sp<EffectBufferHalInterface> &buffer) {
std::stringstream ss;
- if (buffer.get() == nullptr) {
+ if (buffer == nullptr) {
return "nullptr"; // make different than below
} else if (buffer->externalData() != nullptr) {
ss << (isInput ? buffer->externalData() : buffer->audioBuffer()->raw)
@@ -1490,38 +1532,16 @@
return ss.str();
}
-void AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args __unused)
+void AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args)
{
+ EffectBase::dump(fd, args);
+
String8 result;
-
- result.appendFormat("\tEffect ID %d:\n", mId);
-
bool locked = AudioFlinger::dumpTryLock(mLock);
- // failed to lock - AudioFlinger is probably deadlocked
- if (!locked) {
- result.append("\t\tCould not lock Fx mutex:\n");
- }
- result.append("\t\tSession Status State Registered Enabled Suspended Engine:\n");
- result.appendFormat("\t\t%05d %03d %03d %s %s %s %p\n",
- mSessionId, mStatus, mState, mPolicyRegistered ? "y" : "n", mPolicyEnabled ? "y" : "n",
- mSuspended ? "y" : "n", mEffectInterface.get());
-
- result.append("\t\tDescriptor:\n");
- char uuidStr[64];
- AudioEffect::guidToString(&mDescriptor.uuid, uuidStr, sizeof(uuidStr));
- result.appendFormat("\t\t- UUID: %s\n", uuidStr);
- AudioEffect::guidToString(&mDescriptor.type, uuidStr, sizeof(uuidStr));
- result.appendFormat("\t\t- TYPE: %s\n", uuidStr);
- result.appendFormat("\t\t- apiVersion: %08X\n\t\t- flags: %08X (%s)\n",
- mDescriptor.apiVersion,
- mDescriptor.flags,
- effectFlagsToString(mDescriptor.flags).string());
- result.appendFormat("\t\t- name: %s\n",
- mDescriptor.name);
-
- result.appendFormat("\t\t- implementor: %s\n",
- mDescriptor.implementor);
+ result.append("\t\tStatus Engine:\n");
+ result.appendFormat("\t\t%03d %p\n",
+ mStatus, mEffectInterface.get());
result.appendFormat("\t\t- data: %s\n", mSupportsFloat ? "float" : "int16");
@@ -1555,17 +1575,6 @@
dumpInOutBuffer(false /* isInput */, mOutConversionBuffer).c_str());
#endif
- result.appendFormat("\t\t%zu Clients:\n", mHandles.size());
- result.append("\t\t\t Pid Priority Ctrl Locked client server\n");
- char buffer[256];
- for (size_t i = 0; i < mHandles.size(); ++i) {
- EffectHandle *handle = mHandles[i];
- if (handle != NULL && !handle->disconnected()) {
- handle->dumpToBuffer(buffer, sizeof(buffer));
- result.append(buffer);
- }
- }
-
write(fd, result.string(), result.length());
if (mEffectInterface != 0) {
@@ -1585,7 +1594,7 @@
#undef LOG_TAG
#define LOG_TAG "AudioFlinger::EffectHandle"
-AudioFlinger::EffectHandle::EffectHandle(const sp<EffectModule>& effect,
+AudioFlinger::EffectHandle::EffectHandle(const sp<EffectBase>& effect,
const sp<AudioFlinger::Client>& client,
const sp<IEffectClient>& effectClient,
int32_t priority)
@@ -1593,7 +1602,7 @@
mEffect(effect), mEffectClient(effectClient), mClient(client), mCblk(NULL),
mPriority(priority), mHasControl(false), mEnabled(false), mDisconnected(false)
{
- ALOGV("constructor %p", this);
+ ALOGV("constructor %p client %p", this, client.get());
if (client == 0) {
return;
@@ -1626,7 +1635,7 @@
{
AutoMutex _l(mLock);
ALOGV("enable %p", this);
- sp<EffectModule> effect = mEffect.promote();
+ sp<EffectBase> effect = mEffect.promote();
if (effect == 0 || mDisconnected) {
return DEAD_OBJECT;
}
@@ -1646,38 +1655,16 @@
return status;
}
- sp<ThreadBase> thread = effect->thread().promote();
- if (thread != 0) {
- thread->checkSuspendOnEffectEnabled(effect, true, effect->sessionId());
- }
+ effect->checkSuspendOnEffectEnabled(true, false /*threadLocked*/);
// checkSuspendOnEffectEnabled() can suspend this same effect when enabled
if (effect->suspended()) {
return NO_ERROR;
}
- status = effect->setEnabled(true);
+ status = effect->setEnabled(true, true /*fromHandle*/);
if (status != NO_ERROR) {
- if (thread != 0) {
- thread->checkSuspendOnEffectEnabled(effect, false, effect->sessionId());
- }
mEnabled = false;
- } else {
- if (thread != 0) {
- if (thread->type() == ThreadBase::OFFLOAD || thread->type() == ThreadBase::MMAP) {
- Mutex::Autolock _l(thread->mLock);
- thread->broadcast_l();
- }
- if (!effect->isOffloadable()) {
- if (thread->type() == ThreadBase::OFFLOAD) {
- PlaybackThread *t = (PlaybackThread *)thread.get();
- t->invalidateTracks(AUDIO_STREAM_MUSIC);
- }
- if (effect->sessionId() == AUDIO_SESSION_OUTPUT_MIX) {
- thread->mAudioFlinger->onNonOffloadableGlobalEffectEnable();
- }
- }
- }
}
return status;
}
@@ -1686,7 +1673,7 @@
{
ALOGV("disable %p", this);
AutoMutex _l(mLock);
- sp<EffectModule> effect = mEffect.promote();
+ sp<EffectBase> effect = mEffect.promote();
if (effect == 0 || mDisconnected) {
return DEAD_OBJECT;
}
@@ -1705,17 +1692,7 @@
return NO_ERROR;
}
- status_t status = effect->setEnabled(false);
-
- sp<ThreadBase> thread = effect->thread().promote();
- if (thread != 0) {
- thread->checkSuspendOnEffectEnabled(effect, false, effect->sessionId());
- if (thread->type() == ThreadBase::OFFLOAD || thread->type() == ThreadBase::MMAP) {
- Mutex::Autolock _l(thread->mLock);
- thread->broadcast_l();
- }
- }
-
+ status_t status = effect->setEnabled(false, true /*fromHandle*/);
return status;
}
@@ -1737,12 +1714,9 @@
}
mDisconnected = true;
{
- sp<EffectModule> effect = mEffect.promote();
+ sp<EffectBase> effect = mEffect.promote();
if (effect != 0) {
- sp<ThreadBase> thread = effect->thread().promote();
- if (thread != 0) {
- thread->disconnectEffectHandle(this, unpinIfLast);
- } else if (effect->disconnectHandle(this, unpinIfLast) > 0) {
+ if (effect->disconnectHandle(this, unpinIfLast) > 0) {
ALOGW("%s Effect handle %p disconnected after thread destruction",
__func__, this);
}
@@ -1808,7 +1782,7 @@
}
AutoMutex _l(mLock);
- sp<EffectModule> effect = mEffect.promote();
+ sp<EffectBase> effect = mEffect.promote();
if (effect == 0 || mDisconnected) {
return DEAD_OBJECT;
}
@@ -1816,12 +1790,13 @@
if (!mHasControl && cmdCode != EFFECT_CMD_GET_PARAM) {
return INVALID_OPERATION;
}
- if (mClient == 0) {
- return INVALID_OPERATION;
- }
// handle commands that are not forwarded transparently to effect engine
if (cmdCode == EFFECT_CMD_SET_PARAM_COMMIT) {
+ if (mClient == 0) {
+ return INVALID_OPERATION;
+ }
+
if (*replySize < sizeof(int)) {
android_errorWriteLog(0x534e4554, "32095713");
return BAD_VALUE;
@@ -1856,12 +1831,13 @@
}
// copy to local memory in case of client corruption b/32220769
- param = (effect_param_t *)realloc(param, size);
- if (param == NULL) {
+ auto *newParam = (effect_param_t *)realloc(param, size);
+ if (newParam == NULL) {
ALOGW("command(): out of memory");
status = NO_MEMORY;
break;
}
+ param = newParam;
memcpy(param, p, size);
int reply = 0;
@@ -1960,18 +1936,20 @@
#undef LOG_TAG
#define LOG_TAG "AudioFlinger::EffectChain"
-AudioFlinger::EffectChain::EffectChain(ThreadBase *thread,
- audio_session_t sessionId)
- : mThread(thread), mSessionId(sessionId), mActiveTrackCnt(0), mTrackCnt(0), mTailBufferCount(0),
+AudioFlinger::EffectChain::EffectChain(const wp<ThreadBase>& thread,
+ audio_session_t sessionId)
+ : mSessionId(sessionId), mActiveTrackCnt(0), mTrackCnt(0), mTailBufferCount(0),
mVolumeCtrlIdx(-1), mLeftVolume(UINT_MAX), mRightVolume(UINT_MAX),
- mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX)
+ mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX),
+ mEffectCallback(new EffectCallback(wp<EffectChain>(this), thread))
{
mStrategy = AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC);
- if (thread == NULL) {
+ sp<ThreadBase> p = thread.promote();
+ if (p == nullptr) {
return;
}
- mMaxTailBuffers = ((kProcessTailDurationMs * thread->sampleRate()) / 1000) /
- thread->frameCount();
+ mMaxTailBuffers = ((kProcessTailDurationMs * p->sampleRate()) / 1000) /
+ p->frameCount();
}
AudioFlinger::EffectChain::~EffectChain()
@@ -2033,40 +2011,29 @@
void AudioFlinger::EffectChain::clearInputBuffer()
{
Mutex::Autolock _l(mLock);
- sp<ThreadBase> thread = mThread.promote();
- if (thread == 0) {
- ALOGW("clearInputBuffer(): cannot promote mixer thread");
- return;
- }
- clearInputBuffer_l(thread);
+ clearInputBuffer_l();
}
// Must be called with EffectChain::mLock locked
-void AudioFlinger::EffectChain::clearInputBuffer_l(const sp<ThreadBase>& thread)
+void AudioFlinger::EffectChain::clearInputBuffer_l()
{
if (mInBuffer == NULL) {
return;
}
const size_t frameSize =
- audio_bytes_per_sample(EFFECT_BUFFER_FORMAT) * thread->channelCount();
+ audio_bytes_per_sample(EFFECT_BUFFER_FORMAT) * mEffectCallback->channelCount();
- memset(mInBuffer->audioBuffer()->raw, 0, thread->frameCount() * frameSize);
+ memset(mInBuffer->audioBuffer()->raw, 0, mEffectCallback->frameCount() * frameSize);
mInBuffer->commit();
}
// Must be called with EffectChain::mLock locked
void AudioFlinger::EffectChain::process_l()
{
- sp<ThreadBase> thread = mThread.promote();
- if (thread == 0) {
- ALOGW("process_l(): cannot promote mixer thread");
- return;
- }
// never process effects when:
// - on an OFFLOAD thread
// - no more tracks are on the session and the effect tail has been rendered
- bool doProcess = (thread->type() != ThreadBase::OFFLOAD)
- && (thread->type() != ThreadBase::MMAP);
+ bool doProcess = !mEffectCallback->isOffloadOrMmap();
if (!audio_is_global_session(mSessionId)) {
bool tracksOnSession = (trackCnt() != 0);
@@ -2078,7 +2045,7 @@
// if no track is active and the effect tail has not been rendered,
// the input buffer must be cleared here as the mixer process will not do it
if (tracksOnSession || mTailBufferCount > 0) {
- clearInputBuffer_l(thread);
+ clearInputBuffer_l();
if (mTailBufferCount > 0) {
mTailBufferCount--;
}
@@ -2114,14 +2081,13 @@
// createEffect_l() must be called with ThreadBase::mLock held
status_t AudioFlinger::EffectChain::createEffect_l(sp<EffectModule>& effect,
- ThreadBase *thread,
effect_descriptor_t *desc,
int id,
audio_session_t sessionId,
bool pinned)
{
Mutex::Autolock _l(mLock);
- effect = new EffectModule(thread, this, desc, id, sessionId, pinned);
+ effect = new EffectModule(mEffectCallback, desc, id, sessionId, pinned, AUDIO_PORT_HANDLE_NONE);
status_t lStatus = effect->status();
if (lStatus == NO_ERROR) {
lStatus = addEffect_ll(effect);
@@ -2144,12 +2110,7 @@
effect_descriptor_t desc = effect->desc();
uint32_t insertPref = desc.flags & EFFECT_FLAG_INSERT_MASK;
- effect->setChain(this);
- sp<ThreadBase> thread = mThread.promote();
- if (thread == 0) {
- return NO_INIT;
- }
- effect->setThread(thread);
+ effect->setCallback(mEffectCallback);
if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
// Auxiliary effects are inserted at the beginning of mEffects vector as
@@ -2160,13 +2121,13 @@
// 32 bit format. This is to avoid saturation in AudoMixer
// accumulation stage. Saturation is done in EffectModule::process() before
// calling the process in effect engine
- size_t numSamples = thread->frameCount();
+ size_t numSamples = mEffectCallback->frameCount();
sp<EffectBufferHalInterface> halBuffer;
#ifdef FLOAT_EFFECT_CHAIN
- status_t result = thread->mAudioFlinger->mEffectsFactoryHal->allocateBuffer(
+ status_t result = mEffectCallback->allocateHalBuffer(
numSamples * sizeof(float), &halBuffer);
#else
- status_t result = thread->mAudioFlinger->mEffectsFactoryHal->allocateBuffer(
+ status_t result = mEffectCallback->allocateHalBuffer(
numSamples * sizeof(int32_t), &halBuffer);
#endif
if (result != OK) return result;
@@ -2484,7 +2445,7 @@
if (effect != 0) {
desc->mEffect = effect;
effect->setSuspended(true);
- effect->setEnabled(false);
+ effect->setEnabled(false, false /*fromHandle*/);
}
}
} else {
@@ -2642,7 +2603,7 @@
// if effect is requested to suspended but was not yet enabled, suspend it now.
if (desc->mEffect == 0) {
desc->mEffect = effect;
- effect->setEnabled(false);
+ effect->setEnabled(false, false /*fromHandle*/);
effect->setSuspended(true);
}
} else {
@@ -2677,10 +2638,7 @@
void AudioFlinger::EffectChain::setThread(const sp<ThreadBase>& thread)
{
Mutex::Autolock _l(mLock);
- mThread = thread;
- for (size_t i = 0; i < mEffects.size(); i++) {
- mEffects[i]->setThread(thread);
- }
+ mEffectCallback->setThread(thread);
}
void AudioFlinger::EffectChain::checkOutputFlagCompatibility(audio_output_flags_t *flags) const
@@ -2740,4 +2698,549 @@
return true;
}
+// EffectCallbackInterface implementation
+status_t AudioFlinger::EffectChain::EffectCallback::createEffectHal(
+ const effect_uuid_t *pEffectUuid, int32_t sessionId, int32_t deviceId,
+ sp<EffectHalInterface> *effect) {
+ status_t status = NO_INIT;
+ sp<AudioFlinger> af = mAudioFlinger.promote();
+ if (af == nullptr) {
+ return status;
+ }
+ sp<EffectsFactoryHalInterface> effectsFactory = af->getEffectsFactory();
+ if (effectsFactory != 0) {
+ status = effectsFactory->createEffect(pEffectUuid, sessionId, io(), deviceId, effect);
+ }
+ return status;
+}
+
+bool AudioFlinger::EffectChain::EffectCallback::updateOrphanEffectChains(
+ const sp<AudioFlinger::EffectBase>& effect) {
+ sp<AudioFlinger> af = mAudioFlinger.promote();
+ if (af == nullptr) {
+ return false;
+ }
+ // in EffectChain context, an EffectBase is always from an EffectModule so static cast is safe
+ return af->updateOrphanEffectChains(effect->asEffectModule());
+}
+
+status_t AudioFlinger::EffectChain::EffectCallback::allocateHalBuffer(
+ size_t size, sp<EffectBufferHalInterface>* buffer) {
+ sp<AudioFlinger> af = mAudioFlinger.promote();
+ LOG_ALWAYS_FATAL_IF(af == nullptr, "allocateHalBuffer() could not retrieved audio flinger");
+ return af->mEffectsFactoryHal->allocateBuffer(size, buffer);
+}
+
+status_t AudioFlinger::EffectChain::EffectCallback::addEffectToHal(
+ sp<EffectHalInterface> effect) {
+ status_t result = NO_INIT;
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return result;
+ }
+ sp <StreamHalInterface> st = t->stream();
+ if (st == nullptr) {
+ return result;
+ }
+ result = st->addEffect(effect);
+ ALOGE_IF(result != OK, "Error when adding effect: %d", result);
+ return result;
+}
+
+status_t AudioFlinger::EffectChain::EffectCallback::removeEffectFromHal(
+ sp<EffectHalInterface> effect) {
+ status_t result = NO_INIT;
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return result;
+ }
+ sp <StreamHalInterface> st = t->stream();
+ if (st == nullptr) {
+ return result;
+ }
+ result = st->removeEffect(effect);
+ ALOGE_IF(result != OK, "Error when removing effect: %d", result);
+ return result;
+}
+
+audio_io_handle_t AudioFlinger::EffectChain::EffectCallback::io() const {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return AUDIO_IO_HANDLE_NONE;
+ }
+ return t->id();
+}
+
+bool AudioFlinger::EffectChain::EffectCallback::isOutput() const {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return true;
+ }
+ return t->isOutput();
+}
+
+bool AudioFlinger::EffectChain::EffectCallback::isOffload() const {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return false;
+ }
+ return t->type() == ThreadBase::OFFLOAD;
+}
+
+bool AudioFlinger::EffectChain::EffectCallback::isOffloadOrDirect() const {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return false;
+ }
+ return t->type() == ThreadBase::OFFLOAD || t->type() == ThreadBase::DIRECT;
+}
+
+bool AudioFlinger::EffectChain::EffectCallback::isOffloadOrMmap() const {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return false;
+ }
+ return t->type() == ThreadBase::OFFLOAD || t->type() == ThreadBase::MMAP;
+}
+
+uint32_t AudioFlinger::EffectChain::EffectCallback::sampleRate() const {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return 0;
+ }
+ return t->sampleRate();
+}
+
+audio_channel_mask_t AudioFlinger::EffectChain::EffectCallback::channelMask() const {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return AUDIO_CHANNEL_NONE;
+ }
+ return t->channelMask();
+}
+
+uint32_t AudioFlinger::EffectChain::EffectCallback::channelCount() const {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return 0;
+ }
+ return t->channelCount();
+}
+
+size_t AudioFlinger::EffectChain::EffectCallback::frameCount() const {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return 0;
+ }
+ return t->frameCount();
+}
+
+uint32_t AudioFlinger::EffectChain::EffectCallback::latency() const {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return 0;
+ }
+ return t->latency_l();
+}
+
+void AudioFlinger::EffectChain::EffectCallback::setVolumeForOutput(float left, float right) const {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return;
+ }
+ t->setVolumeForOutput_l(left, right);
+}
+
+void AudioFlinger::EffectChain::EffectCallback::checkSuspendOnEffectEnabled(
+ const sp<EffectBase>& effect, bool enabled, bool threadLocked) {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return;
+ }
+ t->checkSuspendOnEffectEnabled(enabled, effect->sessionId(), threadLocked);
+
+ sp<EffectChain> c = mChain.promote();
+ if (c == nullptr) {
+ return;
+ }
+ // in EffectChain context, an EffectBase is always from an EffectModule so static cast is safe
+ c->checkSuspendOnEffectEnabled(effect->asEffectModule(), enabled);
+}
+
+void AudioFlinger::EffectChain::EffectCallback::onEffectEnable(const sp<EffectBase>& effect) {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return;
+ }
+ // in EffectChain context, an EffectBase is always from an EffectModule so static cast is safe
+ t->onEffectEnable(effect->asEffectModule());
+}
+
+void AudioFlinger::EffectChain::EffectCallback::onEffectDisable(const sp<EffectBase>& effect) {
+ checkSuspendOnEffectEnabled(effect, false, false /*threadLocked*/);
+
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return;
+ }
+ t->onEffectDisable();
+}
+
+bool AudioFlinger::EffectChain::EffectCallback::disconnectEffectHandle(EffectHandle *handle,
+ bool unpinIfLast) {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return false;
+ }
+ t->disconnectEffectHandle(handle, unpinIfLast);
+ return true;
+}
+
+void AudioFlinger::EffectChain::EffectCallback::resetVolume() {
+ sp<EffectChain> c = mChain.promote();
+ if (c == nullptr) {
+ return;
+ }
+ c->resetVolume_l();
+
+}
+
+uint32_t AudioFlinger::EffectChain::EffectCallback::strategy() const {
+ sp<EffectChain> c = mChain.promote();
+ if (c == nullptr) {
+ return PRODUCT_STRATEGY_NONE;
+ }
+ return c->strategy();
+}
+
+int32_t AudioFlinger::EffectChain::EffectCallback::activeTrackCnt() const {
+ sp<EffectChain> c = mChain.promote();
+ if (c == nullptr) {
+ return 0;
+ }
+ return c->activeTrackCnt();
+}
+
+
+#undef LOG_TAG
+#define LOG_TAG "AudioFlinger::DeviceEffectProxy"
+
+status_t AudioFlinger::DeviceEffectProxy::setEnabled(bool enabled, bool fromHandle)
+{
+ status_t status = EffectBase::setEnabled(enabled, fromHandle);
+ Mutex::Autolock _l(mProxyLock);
+ if (status == NO_ERROR) {
+ for (auto& handle : mEffectHandles) {
+ if (enabled) {
+ status = handle.second->enable();
+ } else {
+ status = handle.second->disable();
+ }
+ }
+ }
+ ALOGV("%s enable %d status %d", __func__, enabled, status);
+ return status;
+}
+
+status_t AudioFlinger::DeviceEffectProxy::init(
+ const std::map <audio_patch_handle_t, PatchPanel::Patch>& patches) {
+//For all audio patches
+//If src or sink device match
+//If the effect is HW accelerated
+// if no corresponding effect module
+// Create EffectModule: mHalEffect
+//Create and attach EffectHandle
+//If the effect is not HW accelerated and the patch sink or src is a mixer port
+// Create Effect on patch input or output thread on session -1
+//Add EffectHandle to EffectHandle map of Effect Proxy:
+ ALOGV("%s device type %d address %s", __func__, mDevice.mType, mDevice.getAddress());
+ status_t status = NO_ERROR;
+ for (auto &patch : patches) {
+ status = onCreatePatch(patch.first, patch.second);
+ ALOGV("%s onCreatePatch status %d", __func__, status);
+ if (status == BAD_VALUE) {
+ return status;
+ }
+ }
+ return status;
+}
+
+status_t AudioFlinger::DeviceEffectProxy::onCreatePatch(
+ audio_patch_handle_t patchHandle, const AudioFlinger::PatchPanel::Patch& patch) {
+ status_t status = NAME_NOT_FOUND;
+ sp<EffectHandle> handle;
+ // only consider source[0] as this is the only "true" source of a patch
+ status = checkPort(patch, &patch.mAudioPatch.sources[0], &handle);
+ ALOGV("%s source checkPort status %d", __func__, status);
+ for (uint32_t i = 0; i < patch.mAudioPatch.num_sinks && status == NAME_NOT_FOUND; i++) {
+ status = checkPort(patch, &patch.mAudioPatch.sinks[i], &handle);
+ ALOGV("%s sink %d checkPort status %d", __func__, i, status);
+ }
+ if (status == NO_ERROR || status == ALREADY_EXISTS) {
+ Mutex::Autolock _l(mProxyLock);
+ mEffectHandles.emplace(patchHandle, handle);
+ }
+ ALOGW_IF(status == BAD_VALUE,
+ "%s cannot attach effect %s on patch %d", __func__, mDescriptor.name, patchHandle);
+
+ return status;
+}
+
+status_t AudioFlinger::DeviceEffectProxy::checkPort(const PatchPanel::Patch& patch,
+ const struct audio_port_config *port, sp <EffectHandle> *handle) {
+
+ ALOGV("%s type %d device type %d address %s device ID %d patch.isSoftware() %d",
+ __func__, port->type, port->ext.device.type,
+ port->ext.device.address, port->id, patch.isSoftware());
+ if (port->type != AUDIO_PORT_TYPE_DEVICE || port->ext.device.type != mDevice.mType
+ || port->ext.device.address != mDevice.mAddress) {
+ return NAME_NOT_FOUND;
+ }
+ status_t status = NAME_NOT_FOUND;
+
+ if (mDescriptor.flags & EFFECT_FLAG_HW_ACC_TUNNEL) {
+ Mutex::Autolock _l(mProxyLock);
+ mDevicePort = *port;
+ mHalEffect = new EffectModule(mMyCallback,
+ const_cast<effect_descriptor_t *>(&mDescriptor),
+ mMyCallback->newEffectId(), AUDIO_SESSION_DEVICE,
+ false /* pinned */, port->id);
+ if (audio_is_input_device(mDevice.mType)) {
+ mHalEffect->setInputDevice(mDevice);
+ } else {
+ mHalEffect->setDevices({mDevice});
+ }
+ *handle = new EffectHandle(mHalEffect, nullptr, nullptr, 0 /*priority*/);
+ status = (*handle)->initCheck();
+ if (status == OK) {
+ status = mHalEffect->addHandle((*handle).get());
+ } else {
+ mHalEffect.clear();
+ mDevicePort.id = AUDIO_PORT_HANDLE_NONE;
+ }
+ } else if (patch.isSoftware() || patch.thread().promote() != nullptr) {
+ sp <ThreadBase> thread;
+ if (audio_port_config_has_input_direction(port)) {
+ if (patch.isSoftware()) {
+ thread = patch.mRecord.thread();
+ } else {
+ thread = patch.thread().promote();
+ }
+ } else {
+ if (patch.isSoftware()) {
+ thread = patch.mPlayback.thread();
+ } else {
+ thread = patch.thread().promote();
+ }
+ }
+ int enabled;
+ *handle = thread->createEffect_l(nullptr, nullptr, 0, AUDIO_SESSION_DEVICE,
+ const_cast<effect_descriptor_t *>(&mDescriptor),
+ &enabled, &status, false);
+ ALOGV("%s thread->createEffect_l status %d", __func__, status);
+ } else {
+ status = BAD_VALUE;
+ }
+ if (status == NO_ERROR || status == ALREADY_EXISTS) {
+ if (isEnabled()) {
+ (*handle)->enable();
+ } else {
+ (*handle)->disable();
+ }
+ }
+ return status;
+}
+
+void AudioFlinger::DeviceEffectProxy::onReleasePatch(audio_patch_handle_t patchHandle) {
+ Mutex::Autolock _l(mProxyLock);
+ mEffectHandles.erase(patchHandle);
+}
+
+
+size_t AudioFlinger::DeviceEffectProxy::removeEffect(const sp<EffectModule>& effect)
+{
+ Mutex::Autolock _l(mProxyLock);
+ if (effect == mHalEffect) {
+ mHalEffect.clear();
+ mDevicePort.id = AUDIO_PORT_HANDLE_NONE;
+ }
+ return mHalEffect == nullptr ? 0 : 1;
+}
+
+status_t AudioFlinger::DeviceEffectProxy::addEffectToHal(
+ sp<EffectHalInterface> effect) {
+ if (mHalEffect == nullptr) {
+ return NO_INIT;
+ }
+ return mManagerCallback->addEffectToHal(
+ mDevicePort.id, mDevicePort.ext.device.hw_module, effect);
+}
+
+status_t AudioFlinger::DeviceEffectProxy::removeEffectFromHal(
+ sp<EffectHalInterface> effect) {
+ if (mHalEffect == nullptr) {
+ return NO_INIT;
+ }
+ return mManagerCallback->removeEffectFromHal(
+ mDevicePort.id, mDevicePort.ext.device.hw_module, effect);
+}
+
+bool AudioFlinger::DeviceEffectProxy::isOutput() const {
+ if (mDevicePort.id != AUDIO_PORT_HANDLE_NONE) {
+ return mDevicePort.role == AUDIO_PORT_ROLE_SINK;
+ }
+ return true;
+}
+
+uint32_t AudioFlinger::DeviceEffectProxy::sampleRate() const {
+ if (mDevicePort.id != AUDIO_PORT_HANDLE_NONE &&
+ (mDevicePort.config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) != 0) {
+ return mDevicePort.sample_rate;
+ }
+ return DEFAULT_OUTPUT_SAMPLE_RATE;
+}
+
+audio_channel_mask_t AudioFlinger::DeviceEffectProxy::channelMask() const {
+ if (mDevicePort.id != AUDIO_PORT_HANDLE_NONE &&
+ (mDevicePort.config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) != 0) {
+ return mDevicePort.channel_mask;
+ }
+ return AUDIO_CHANNEL_OUT_STEREO;
+}
+
+uint32_t AudioFlinger::DeviceEffectProxy::channelCount() const {
+ if (isOutput()) {
+ return audio_channel_count_from_out_mask(channelMask());
+ }
+ return audio_channel_count_from_in_mask(channelMask());
+}
+
+void AudioFlinger::DeviceEffectProxy::dump(int fd, int spaces) {
+ const Vector<String16> args;
+ EffectBase::dump(fd, args);
+
+ const bool locked = dumpTryLock(mProxyLock);
+ if (!locked) {
+ String8 result("DeviceEffectProxy may be deadlocked\n");
+ write(fd, result.string(), result.size());
+ }
+
+ String8 outStr;
+ if (mHalEffect != nullptr) {
+ outStr.appendFormat("%*sHAL Effect Id: %d\n", spaces, "", mHalEffect->id());
+ } else {
+ outStr.appendFormat("%*sNO HAL Effect\n", spaces, "");
+ }
+ write(fd, outStr.string(), outStr.size());
+ outStr.clear();
+
+ outStr.appendFormat("%*sSub Effects:\n", spaces, "");
+ write(fd, outStr.string(), outStr.size());
+ outStr.clear();
+
+ for (const auto& iter : mEffectHandles) {
+ outStr.appendFormat("%*sEffect for patch handle %d:\n", spaces + 2, "", iter.first);
+ write(fd, outStr.string(), outStr.size());
+ outStr.clear();
+ sp<EffectBase> effect = iter.second->effect().promote();
+ if (effect != nullptr) {
+ effect->dump(fd, args);
+ }
+ }
+
+ if (locked) {
+ mLock.unlock();
+ }
+}
+
+#undef LOG_TAG
+#define LOG_TAG "AudioFlinger::DeviceEffectProxy::ProxyCallback"
+
+int AudioFlinger::DeviceEffectProxy::ProxyCallback::newEffectId() {
+ return mManagerCallback->newEffectId();
+}
+
+
+bool AudioFlinger::DeviceEffectProxy::ProxyCallback::disconnectEffectHandle(
+ EffectHandle *handle, bool unpinIfLast) {
+ sp<EffectBase> effectBase = handle->effect().promote();
+ if (effectBase == nullptr) {
+ return false;
+ }
+
+ sp<EffectModule> effect = effectBase->asEffectModule();
+ if (effect == nullptr) {
+ return false;
+ }
+
+ // restore suspended effects if the disconnected handle was enabled and the last one.
+ bool remove = (effect->removeHandle(handle) == 0) && (!effect->isPinned() || unpinIfLast);
+ if (remove) {
+ sp<DeviceEffectProxy> proxy = mProxy.promote();
+ if (proxy != nullptr) {
+ proxy->removeEffect(effect);
+ }
+ if (handle->enabled()) {
+ effectBase->checkSuspendOnEffectEnabled(false, false /*threadLocked*/);
+ }
+ }
+ return true;
+}
+
+status_t AudioFlinger::DeviceEffectProxy::ProxyCallback::createEffectHal(
+ const effect_uuid_t *pEffectUuid, int32_t sessionId, int32_t deviceId,
+ sp<EffectHalInterface> *effect) {
+ return mManagerCallback->createEffectHal(pEffectUuid, sessionId, deviceId, effect);
+}
+
+status_t AudioFlinger::DeviceEffectProxy::ProxyCallback::addEffectToHal(
+ sp<EffectHalInterface> effect) {
+ sp<DeviceEffectProxy> proxy = mProxy.promote();
+ if (proxy == nullptr) {
+ return NO_INIT;
+ }
+ return proxy->addEffectToHal(effect);
+}
+
+status_t AudioFlinger::DeviceEffectProxy::ProxyCallback::removeEffectFromHal(
+ sp<EffectHalInterface> effect) {
+ sp<DeviceEffectProxy> proxy = mProxy.promote();
+ if (proxy == nullptr) {
+ return NO_INIT;
+ }
+ return proxy->addEffectToHal(effect);
+}
+
+bool AudioFlinger::DeviceEffectProxy::ProxyCallback::isOutput() const {
+ sp<DeviceEffectProxy> proxy = mProxy.promote();
+ if (proxy == nullptr) {
+ return true;
+ }
+ return proxy->isOutput();
+}
+
+uint32_t AudioFlinger::DeviceEffectProxy::ProxyCallback::sampleRate() const {
+ sp<DeviceEffectProxy> proxy = mProxy.promote();
+ if (proxy == nullptr) {
+ return DEFAULT_OUTPUT_SAMPLE_RATE;
+ }
+ return proxy->sampleRate();
+}
+
+audio_channel_mask_t AudioFlinger::DeviceEffectProxy::ProxyCallback::channelMask() const {
+ sp<DeviceEffectProxy> proxy = mProxy.promote();
+ if (proxy == nullptr) {
+ return AUDIO_CHANNEL_OUT_STEREO;
+ }
+ return proxy->channelMask();
+}
+
+uint32_t AudioFlinger::DeviceEffectProxy::ProxyCallback::channelCount() const {
+ sp<DeviceEffectProxy> proxy = mProxy.promote();
+ if (proxy == nullptr) {
+ return 2;
+ }
+ return proxy->channelCount();
+}
+
} // namespace android
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index dbf63c8..4901451 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -21,34 +21,78 @@
//--- Audio Effect Management
-// EffectModule and EffectChain classes both have their own mutex to protect
+// Interface implemented by the EffectModule parent or owner (e.g an EffectChain) to abstract
+// interactions between the EffectModule and the reset of the audio framework.
+class EffectCallbackInterface : public RefBase {
+public:
+ ~EffectCallbackInterface() override = default;
+
+ // Trivial methods usually implemented with help from ThreadBase
+ virtual audio_io_handle_t io() const = 0;
+ virtual bool isOutput() const = 0;
+ virtual bool isOffload() const = 0;
+ virtual bool isOffloadOrDirect() const = 0;
+ virtual bool isOffloadOrMmap() const = 0;
+ virtual uint32_t sampleRate() const = 0;
+ virtual audio_channel_mask_t channelMask() const = 0;
+ virtual uint32_t channelCount() const = 0;
+ virtual size_t frameCount() const = 0;
+
+ // Non trivial methods usually implemented with help from ThreadBase:
+ // pay attention to mutex locking order
+ virtual uint32_t latency() const { return 0; }
+ virtual status_t addEffectToHal(sp<EffectHalInterface> effect) = 0;
+ virtual status_t removeEffectFromHal(sp<EffectHalInterface> effect) = 0;
+ virtual void setVolumeForOutput(float left, float right) const = 0;
+ virtual bool disconnectEffectHandle(EffectHandle *handle, bool unpinIfLast) = 0;
+ virtual void checkSuspendOnEffectEnabled(const sp<EffectBase>& effect,
+ bool enabled,
+ bool threadLocked) = 0;
+ virtual void onEffectEnable(const sp<EffectBase>& effect) = 0;
+ virtual void onEffectDisable(const sp<EffectBase>& effect) = 0;
+
+ // Methods usually implemented with help from AudioFlinger: pay attention to mutex locking order
+ virtual status_t createEffectHal(const effect_uuid_t *pEffectUuid,
+ int32_t sessionId, int32_t deviceId, sp<EffectHalInterface> *effect) = 0;
+ virtual status_t allocateHalBuffer(size_t size, sp<EffectBufferHalInterface>* buffer) = 0;
+ virtual bool updateOrphanEffectChains(const sp<EffectBase>& effect) = 0;
+
+ // Methods usually implemented with help from EffectChain: pay attention to mutex locking order
+ virtual uint32_t strategy() const = 0;
+ virtual int32_t activeTrackCnt() const = 0;
+ virtual void resetVolume() = 0;
+
+ virtual wp<EffectChain> chain() const = 0;
+};
+
+// EffectBase(EffectModule) and EffectChain classes both have their own mutex to protect
// state changes or resource modifications. Always respect the following order
// if multiple mutexes must be acquired to avoid cross deadlock:
-// AudioFlinger -> ThreadBase -> EffectChain -> EffectModule
-// AudioHandle -> ThreadBase -> EffectChain -> EffectModule
+// AudioFlinger -> ThreadBase -> EffectChain -> EffectBase(EffectModule)
+// AudioHandle -> ThreadBase -> EffectChain -> EffectBase(EffectModule)
+
+// NOTE: When implementing the EffectCallbackInterface, in an EffectChain or other, it is important
+// to pay attention to this locking order as some callback methods can be called from a state where
+// EffectModule and/or EffectChain mutexes are held.
+
// In addition, methods that lock the AudioPolicyService mutex (getOutputForEffect(),
// startOutput(), getInputForAttr(), releaseInput()...) should never be called with AudioFlinger or
// Threadbase mutex locked to avoid cross deadlock with other clients calling AudioPolicyService
// methods that in turn call AudioFlinger thus locking the same mutexes in the reverse order.
-// The EffectModule class is a wrapper object controlling the effect engine implementation
-// in the effect library. It prevents concurrent calls to process() and command() functions
-// from different client threads. It keeps a list of EffectHandle objects corresponding
-// to all client applications using this effect and notifies applications of effect state,
-// control or parameter changes. It manages the activation state machine to send appropriate
-// reset, enable, disable commands to effect engine and provide volume
-// ramping when effects are activated/deactivated.
-// When controlling an auxiliary effect, the EffectModule also provides an input buffer used by
-// the attached track(s) to accumulate their auxiliary channel.
-class EffectModule : public RefBase {
+
+// The EffectBase class contains common properties, state and behavior for and EffectModule or
+// other derived classes managing an audio effect instance within the effect framework.
+// It also contains the class mutex (see comment on locking order above).
+class EffectBase : public RefBase {
public:
- EffectModule(ThreadBase *thread,
- const wp<AudioFlinger::EffectChain>& chain,
- effect_descriptor_t *desc,
- int id,
- audio_session_t sessionId,
- bool pinned);
- virtual ~EffectModule();
+ EffectBase(const sp<EffectCallbackInterface>& callback,
+ effect_descriptor_t *desc,
+ int id,
+ audio_session_t sessionId,
+ bool pinned);
+
+ ~EffectBase() override = default;
enum effect_state {
IDLE,
@@ -60,72 +104,14 @@
DESTROYED
};
- int id() const { return mId; }
- void process();
- bool updateState();
- status_t command(uint32_t cmdCode,
- uint32_t cmdSize,
- void *pCmdData,
- uint32_t *replySize,
- void *pReplyData);
-
- void reset_l();
- status_t configure();
- status_t init();
+ int id() const { return mId; }
effect_state state() const {
return mState;
}
- uint32_t status() {
- return mStatus;
- }
audio_session_t sessionId() const {
return mSessionId;
}
- status_t setEnabled(bool enabled);
- status_t setEnabled_l(bool enabled);
- bool isEnabled() const;
- bool isProcessEnabled() const;
- bool isOffloadedOrDirect() const;
- bool isVolumeControlEnabled() const;
-
- void setInBuffer(const sp<EffectBufferHalInterface>& buffer);
- int16_t *inBuffer() const {
- return mInBuffer != 0 ? reinterpret_cast<int16_t*>(mInBuffer->ptr()) : NULL;
- }
- void setOutBuffer(const sp<EffectBufferHalInterface>& buffer);
- int16_t *outBuffer() const {
- return mOutBuffer != 0 ? reinterpret_cast<int16_t*>(mOutBuffer->ptr()) : NULL;
- }
- void setChain(const wp<EffectChain>& chain) { mChain = chain; }
- void setThread(const wp<ThreadBase>& thread)
- { mThread = thread; mThreadType = thread.promote()->type(); }
- const wp<ThreadBase>& thread() { return mThread; }
-
- status_t addHandle(EffectHandle *handle);
- ssize_t disconnectHandle(EffectHandle *handle, bool unpinIfLast);
- ssize_t removeHandle(EffectHandle *handle);
- ssize_t removeHandle_l(EffectHandle *handle);
-
const effect_descriptor_t& desc() const { return mDescriptor; }
- wp<EffectChain>& chain() { return mChain; }
-
- status_t setDevices(const AudioDeviceTypeAddrVector &devices);
- status_t setInputDevice(const AudioDeviceTypeAddr &device);
- status_t setVolume(uint32_t *left, uint32_t *right, bool controller);
- status_t setMode(audio_mode_t mode);
- status_t setAudioSource(audio_source_t source);
- status_t start();
- status_t stop();
- void setSuspended(bool suspended);
- bool suspended() const;
-
- EffectHandle* controlHandle_l();
-
- bool isPinned() const { return mPinned; }
- void unPin() { mPinned = false; }
- bool purgeHandles();
- void lock() { mLock.lock(); }
- void unlock() { mLock.unlock(); }
bool isOffloadable() const
{ return (mDescriptor.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) != 0; }
bool isImplementationSoftware() const
@@ -138,18 +124,143 @@
bool isVolumeMonitor() const
{ return (mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK)
== EFFECT_FLAG_VOLUME_MONITOR; }
- status_t setOffloaded(bool offloaded, audio_io_handle_t io);
- bool isOffloaded() const;
- void addEffectToHal_l();
- void release_l();
+
+ virtual status_t setEnabled(bool enabled, bool fromHandle);
+ status_t setEnabled_l(bool enabled);
+ bool isEnabled() const;
+
+ void setSuspended(bool suspended);
+ bool suspended() const;
+
+ virtual status_t command(uint32_t cmdCode __unused,
+ uint32_t cmdSize __unused,
+ void *pCmdData __unused,
+ uint32_t *replySize __unused,
+ void *pReplyData __unused) { return NO_ERROR; };
+
+ void setCallback(const sp<EffectCallbackInterface>& callback) { mCallback = callback; }
+ sp<EffectCallbackInterface>& callback() { return mCallback; }
+
+ status_t addHandle(EffectHandle *handle);
+ ssize_t disconnectHandle(EffectHandle *handle, bool unpinIfLast);
+ ssize_t removeHandle(EffectHandle *handle);
+ virtual ssize_t removeHandle_l(EffectHandle *handle);
+ EffectHandle* controlHandle_l();
+ bool purgeHandles();
+
+ void checkSuspendOnEffectEnabled(bool enabled, bool threadLocked);
+
+ bool isPinned() const { return mPinned; }
+ void unPin() { mPinned = false; }
+
+ void lock() { mLock.lock(); }
+ void unlock() { mLock.unlock(); }
status_t updatePolicyState();
+ virtual sp<EffectModule> asEffectModule() { return nullptr; }
+ virtual sp<DeviceEffectProxy> asDeviceEffectProxy() { return nullptr; }
+
void dump(int fd, const Vector<String16>& args);
private:
friend class AudioFlinger; // for mHandles
- bool mPinned;
+ bool mPinned = false;
+
+ DISALLOW_COPY_AND_ASSIGN(EffectBase);
+
+mutable Mutex mLock; // mutex for process, commands and handles list protection
+ sp<EffectCallbackInterface> mCallback; // parent effect chain
+ const int mId; // this instance unique ID
+ const audio_session_t mSessionId; // audio session ID
+ const effect_descriptor_t mDescriptor;// effect descriptor received from effect engine
+ effect_state mState = IDLE; // current activation state
+ // effect is suspended: temporarily disabled by framework
+ bool mSuspended = false;
+
+ Vector<EffectHandle *> mHandles; // list of client handles
+ // First handle in mHandles has highest priority and controls the effect module
+
+ // Audio policy effect state management
+ // Mutex protecting transactions with audio policy manager as mLock cannot
+ // be held to avoid cross deadlocks with audio policy mutex
+ Mutex mPolicyLock;
+ // Effect is registered in APM or not
+ bool mPolicyRegistered = false;
+ // Effect enabled state communicated to APM. Enabled state corresponds to
+ // state requested by the EffectHandle with control
+ bool mPolicyEnabled = false;
+};
+
+// The EffectModule class is a wrapper object controlling the effect engine implementation
+// in the effect library. It prevents concurrent calls to process() and command() functions
+// from different client threads. It keeps a list of EffectHandle objects corresponding
+// to all client applications using this effect and notifies applications of effect state,
+// control or parameter changes. It manages the activation state machine to send appropriate
+// reset, enable, disable commands to effect engine and provide volume
+// ramping when effects are activated/deactivated.
+// When controlling an auxiliary effect, the EffectModule also provides an input buffer used by
+// the attached track(s) to accumulate their auxiliary channel.
+class EffectModule : public EffectBase {
+public:
+ EffectModule(const sp<EffectCallbackInterface>& callabck,
+ effect_descriptor_t *desc,
+ int id,
+ audio_session_t sessionId,
+ bool pinned,
+ audio_port_handle_t deviceId);
+ virtual ~EffectModule();
+
+ void process();
+ bool updateState();
+ status_t command(uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t *replySize,
+ void *pReplyData) override;
+
+ void reset_l();
+ status_t configure();
+ status_t init();
+
+ uint32_t status() {
+ return mStatus;
+ }
+
+ bool isProcessEnabled() const;
+ bool isOffloadedOrDirect() const;
+ bool isVolumeControlEnabled() const;
+
+ void setInBuffer(const sp<EffectBufferHalInterface>& buffer);
+ int16_t *inBuffer() const {
+ return mInBuffer != 0 ? reinterpret_cast<int16_t*>(mInBuffer->ptr()) : NULL;
+ }
+ void setOutBuffer(const sp<EffectBufferHalInterface>& buffer);
+ int16_t *outBuffer() const {
+ return mOutBuffer != 0 ? reinterpret_cast<int16_t*>(mOutBuffer->ptr()) : NULL;
+ }
+
+ ssize_t removeHandle_l(EffectHandle *handle) override;
+
+ status_t setDevices(const AudioDeviceTypeAddrVector &devices);
+ status_t setInputDevice(const AudioDeviceTypeAddr &device);
+ status_t setVolume(uint32_t *left, uint32_t *right, bool controller);
+ status_t setMode(audio_mode_t mode);
+ status_t setAudioSource(audio_source_t source);
+ status_t start();
+ status_t stop();
+
+ status_t setOffloaded(bool offloaded, audio_io_handle_t io);
+ bool isOffloaded() const;
+ void addEffectToHal_l();
+ void release_l();
+
+ sp<EffectModule> asEffectModule() override { return this; }
+
+ void dump(int fd, const Vector<String16>& args);
+
+private:
+ friend class AudioFlinger; // for mHandles
// Maximum time allocated to effect engines to complete the turn off sequence
static const uint32_t MAX_DISABLE_TIME_MS = 10000;
@@ -158,30 +269,19 @@
status_t start_l();
status_t stop_l();
- status_t remove_effect_from_hal_l();
+ status_t removeEffectFromHal_l();
status_t sendSetAudioDevicesCommand(const AudioDeviceTypeAddrVector &devices, uint32_t cmdCode);
-mutable Mutex mLock; // mutex for process, commands and handles list protection
- wp<ThreadBase> mThread; // parent thread
- ThreadBase::type_t mThreadType; // parent thread type
- wp<EffectChain> mChain; // parent effect chain
- const int mId; // this instance unique ID
- const audio_session_t mSessionId; // audio session ID
- const effect_descriptor_t mDescriptor;// effect descriptor received from effect engine
effect_config_t mConfig; // input and output audio configuration
sp<EffectHalInterface> mEffectInterface; // Effect module HAL
sp<EffectBufferHalInterface> mInBuffer; // Buffers for interacting with HAL
sp<EffectBufferHalInterface> mOutBuffer;
status_t mStatus; // initialization status
- effect_state mState; // current activation state
- Vector<EffectHandle *> mHandles; // list of client handles
// First handle in mHandles has highest priority and controls the effect module
uint32_t mMaxDisableWaitCnt; // maximum grace period before forcing an effect off after
// sending disable command.
uint32_t mDisableWaitCnt; // current process() calls count during disable period.
- bool mSuspended; // effect is suspended: temporarily disabled by framework
bool mOffloaded; // effect is currently offloaded to the audio DSP
- wp<AudioFlinger> mAudioFlinger;
#ifdef FLOAT_EFFECT_CHAIN
bool mSupportsFloat; // effect supports float processing
@@ -208,16 +308,6 @@
static constexpr pid_t INVALID_PID = (pid_t)-1;
// this tid is allowed to call setVolume() without acquiring the mutex.
pid_t mSetVolumeReentrantTid = INVALID_PID;
-
- // Audio policy effect state management
- // Mutex protecting transactions with audio policy manager as mLock cannot
- // be held to avoid cross deadlocks with audio policy mutex
- Mutex mPolicyLock;
- // Effect is registered in APM or not
- bool mPolicyRegistered = false;
- // Effect enabled state communicated to APM. Enabled state corresponds to
- // state requested by the EffectHandle with control
- bool mPolicyEnabled = false;
};
// The EffectHandle class implements the IEffect interface. It provides resources
@@ -229,7 +319,7 @@
class EffectHandle: public android::BnEffect {
public:
- EffectHandle(const sp<EffectModule>& effect,
+ EffectHandle(const sp<EffectBase>& effect,
const sp<AudioFlinger::Client>& client,
const sp<IEffectClient>& effectClient,
int32_t priority);
@@ -267,9 +357,9 @@
bool enabled() const { return mEnabled; }
// Getters
- wp<EffectModule> effect() const { return mEffect; }
+ wp<EffectBase> effect() const { return mEffect; }
int id() const {
- sp<EffectModule> effect = mEffect.promote();
+ sp<EffectBase> effect = mEffect.promote();
if (effect == 0) {
return 0;
}
@@ -286,7 +376,7 @@
DISALLOW_COPY_AND_ASSIGN(EffectHandle);
Mutex mLock; // protects IEffect method calls
- wp<EffectModule> mEffect; // pointer to controlled EffectModule
+ wp<EffectBase> mEffect; // pointer to controlled EffectModule
sp<IEffectClient> mEffectClient; // callback interface for client notifications
/*const*/ sp<Client> mClient; // client for shared memory allocation, see disconnect()
sp<IMemory> mCblkMemory; // shared memory for control block
@@ -312,7 +402,6 @@
class EffectChain : public RefBase {
public:
EffectChain(const wp<ThreadBase>& wThread, audio_session_t sessionId);
- EffectChain(ThreadBase *thread, audio_session_t sessionId);
virtual ~EffectChain();
// special key used for an entry in mSuspendedEffects keyed vector
@@ -333,7 +422,6 @@
}
status_t createEffect_l(sp<EffectModule>& effect,
- ThreadBase *thread,
effect_descriptor_t *desc,
int id,
audio_session_t sessionId,
@@ -389,9 +477,8 @@
bool suspend);
// suspend all eligible effects
void setEffectSuspendedAll_l(bool suspend);
- // check if effects should be suspend or restored when a given effect is enable or disabled
- void checkSuspendOnEffectEnabled(const sp<EffectModule>& effect,
- bool enabled);
+ // check if effects should be suspended or restored when a given effect is enable or disabled
+ void checkSuspendOnEffectEnabled(const sp<EffectModule>& effect, bool enabled);
void clearInputBuffer();
@@ -416,9 +503,70 @@
// isCompatibleWithThread_l() must be called with thread->mLock held
bool isCompatibleWithThread_l(const sp<ThreadBase>& thread) const;
+ sp<EffectCallbackInterface> effectCallback() const { return mEffectCallback; }
+ wp<ThreadBase> thread() const { return mEffectCallback->thread(); }
+
void dump(int fd, const Vector<String16>& args);
private:
+
+ class EffectCallback : public EffectCallbackInterface {
+ public:
+ // Note: ctors taking a weak pointer to their owner must not promote it
+ // during construction (but may keep a reference for later promotion).
+ EffectCallback(const wp<EffectChain>& owner,
+ const wp<ThreadBase>& thread)
+ : mChain(owner) {
+ setThread(thread);
+ }
+
+ status_t createEffectHal(const effect_uuid_t *pEffectUuid,
+ int32_t sessionId, int32_t deviceId, sp<EffectHalInterface> *effect) override;
+ status_t allocateHalBuffer(size_t size, sp<EffectBufferHalInterface>* buffer) override;
+ bool updateOrphanEffectChains(const sp<EffectBase>& effect) override;
+
+ audio_io_handle_t io() const override;
+ bool isOutput() const override;
+ bool isOffload() const override;
+ bool isOffloadOrDirect() const override;
+ bool isOffloadOrMmap() const override;
+
+ uint32_t sampleRate() const override;
+ audio_channel_mask_t channelMask() const override;
+ uint32_t channelCount() const override;
+ size_t frameCount() const override;
+ uint32_t latency() const override;
+
+ status_t addEffectToHal(sp<EffectHalInterface> effect) override;
+ status_t removeEffectFromHal(sp<EffectHalInterface> effect) override;
+ bool disconnectEffectHandle(EffectHandle *handle, bool unpinIfLast) override;
+ void setVolumeForOutput(float left, float right) const override;
+
+ // check if effects should be suspended/restored when a given effect is enable/disabled
+ void checkSuspendOnEffectEnabled(const sp<EffectBase>& effect,
+ bool enabled, bool threadLocked) override;
+ void resetVolume() override;
+ uint32_t strategy() const override;
+ int32_t activeTrackCnt() const override;
+ void onEffectEnable(const sp<EffectBase>& effect) override;
+ void onEffectDisable(const sp<EffectBase>& effect) override;
+
+ wp<EffectChain> chain() const override { return mChain; }
+
+ wp<ThreadBase> thread() { return mThread; }
+
+ void setThread(const wp<ThreadBase>& thread) {
+ mThread = thread;
+ sp<ThreadBase> p = thread.promote();
+ mAudioFlinger = p ? p->mAudioFlinger : nullptr;
+ }
+
+ private:
+ wp<EffectChain> mChain;
+ wp<ThreadBase> mThread;
+ wp<AudioFlinger> mAudioFlinger;
+ };
+
friend class AudioFlinger; // for mThread, mEffects
DISALLOW_COPY_AND_ASSIGN(EffectChain);
@@ -444,13 +592,12 @@
static bool isEffectEligibleForBtNrecSuspend(const effect_uuid_t *type);
- void clearInputBuffer_l(const sp<ThreadBase>& thread);
+ void clearInputBuffer_l();
void setThread(const sp<ThreadBase>& thread);
void setVolumeForOutput_l(uint32_t left, uint32_t right);
- wp<ThreadBase> mThread; // parent mixer thread
mutable Mutex mLock; // mutex protecting effect list
Vector< sp<EffectModule> > mEffects; // list of effect modules
audio_session_t mSessionId; // audio session ID
@@ -474,4 +621,102 @@
// timeLow fields among effect type UUIDs.
// Updated by setEffectSuspended_l() and setEffectSuspendedAll_l() only.
KeyedVector< int, sp<SuspendedEffectDesc> > mSuspendedEffects;
+
+ const sp<EffectCallback> mEffectCallback;
+};
+
+class DeviceEffectProxy : public EffectBase {
+public:
+ DeviceEffectProxy (const AudioDeviceTypeAddr& device,
+ const sp<DeviceEffectManagerCallback>& callback,
+ effect_descriptor_t *desc, int id)
+ : EffectBase(callback, desc, id, AUDIO_SESSION_DEVICE, false),
+ mDevice(device), mManagerCallback(callback),
+ mMyCallback(new ProxyCallback(wp<DeviceEffectProxy>(this),
+ callback)) {}
+
+ status_t setEnabled(bool enabled, bool fromHandle) override;
+ sp<DeviceEffectProxy> asDeviceEffectProxy() override { return this; }
+
+ status_t init(const std::map<audio_patch_handle_t, PatchPanel::Patch>& patches);
+ status_t onCreatePatch(audio_patch_handle_t patchHandle, const PatchPanel::Patch& patch);
+ void onReleasePatch(audio_patch_handle_t patchHandle);
+
+ size_t removeEffect(const sp<EffectModule>& effect);
+
+ status_t addEffectToHal(sp<EffectHalInterface> effect);
+ status_t removeEffectFromHal(sp<EffectHalInterface> effect);
+
+ const AudioDeviceTypeAddr& device() { return mDevice; };
+ bool isOutput() const;
+ uint32_t sampleRate() const;
+ audio_channel_mask_t channelMask() const;
+ uint32_t channelCount() const;
+
+ void dump(int fd, int spaces);
+
+private:
+
+ class ProxyCallback : public EffectCallbackInterface {
+ public:
+ // Note: ctors taking a weak pointer to their owner must not promote it
+ // during construction (but may keep a reference for later promotion).
+ ProxyCallback(const wp<DeviceEffectProxy>& owner,
+ const sp<DeviceEffectManagerCallback>& callback)
+ : mProxy(owner), mManagerCallback(callback) {}
+
+ status_t createEffectHal(const effect_uuid_t *pEffectUuid,
+ int32_t sessionId, int32_t deviceId, sp<EffectHalInterface> *effect) override;
+ status_t allocateHalBuffer(size_t size __unused,
+ sp<EffectBufferHalInterface>* buffer __unused) override { return NO_ERROR; }
+ bool updateOrphanEffectChains(const sp<EffectBase>& effect __unused) override {
+ return false;
+ }
+
+ audio_io_handle_t io() const override { return AUDIO_IO_HANDLE_NONE; }
+ bool isOutput() const override;
+ bool isOffload() const override { return false; }
+ bool isOffloadOrDirect() const override { return false; }
+ bool isOffloadOrMmap() const override { return false; }
+
+ uint32_t sampleRate() const override;
+ audio_channel_mask_t channelMask() const override;
+ uint32_t channelCount() const override;
+ size_t frameCount() const override { return 0; }
+ uint32_t latency() const override { return 0; }
+
+ status_t addEffectToHal(sp<EffectHalInterface> effect) override;
+ status_t removeEffectFromHal(sp<EffectHalInterface> effect) override;
+
+ bool disconnectEffectHandle(EffectHandle *handle, bool unpinIfLast) override;
+ void setVolumeForOutput(float left __unused, float right __unused) const override {}
+
+ void checkSuspendOnEffectEnabled(const sp<EffectBase>& effect __unused,
+ bool enabled __unused, bool threadLocked __unused) override {}
+ void resetVolume() override {}
+ uint32_t strategy() const override { return 0; }
+ int32_t activeTrackCnt() const override { return 0; }
+ void onEffectEnable(const sp<EffectBase>& effect __unused) override {}
+ void onEffectDisable(const sp<EffectBase>& effect __unused) override {}
+
+ wp<EffectChain> chain() const override { return nullptr; }
+
+ int newEffectId();
+
+ private:
+ const wp<DeviceEffectProxy> mProxy;
+ const sp<DeviceEffectManagerCallback> mManagerCallback;
+ };
+
+ status_t checkPort(const PatchPanel::Patch& patch, const struct audio_port_config *port,
+ sp<EffectHandle> *handle);
+
+ const AudioDeviceTypeAddr mDevice;
+ const sp<DeviceEffectManagerCallback> mManagerCallback;
+ const sp<ProxyCallback> mMyCallback;
+
+ Mutex mProxyLock;
+ std::map<audio_patch_handle_t, sp<EffectHandle>> mEffectHandles; // protected by mProxyLock
+ sp<EffectModule> mHalEffect; // protected by mProxyLock
+ struct audio_port_config mDevicePort = { .id = AUDIO_PORT_HANDLE_NONE };
};
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index 53e2dd5..b58fd8b 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -170,8 +170,7 @@
}
halHandle = removedPatch.mHalHandle;
}
- mPatches.erase(iter);
- removeSoftwarePatchFromInsertedModules(*handle);
+ erasePatch(*handle);
}
}
@@ -326,10 +325,14 @@
}
}
status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle);
+ if (status == NO_ERROR) {
+ newPatch.setThread(thread);
+ }
+
// remove stale audio patch with same input as sink if any
for (auto& iter : mPatches) {
if (iter.second.mAudioPatch.sinks[0].ext.mix.handle == thread->id()) {
- mPatches.erase(iter.first);
+ erasePatch(iter.first);
break;
}
}
@@ -388,11 +391,14 @@
}
status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle);
+ if (status == NO_ERROR) {
+ newPatch.setThread(thread);
+ }
// remove stale audio patch with same output as source if any
for (auto& iter : mPatches) {
if (iter.second.mAudioPatch.sources[0].ext.mix.handle == thread->id()) {
- mPatches.erase(iter.first);
+ erasePatch(iter.first);
break;
}
}
@@ -406,11 +412,11 @@
if (status == NO_ERROR) {
*handle = (audio_patch_handle_t) mAudioFlinger.nextUniqueId(AUDIO_UNIQUE_ID_USE_PATCH);
newPatch.mHalHandle = halHandle;
+ mAudioFlinger.mDeviceEffectManager.createAudioPatch(*handle, newPatch);
mPatches.insert(std::make_pair(*handle, std::move(newPatch)));
if (insertedModule != AUDIO_MODULE_HANDLE_NONE) {
addSoftwarePatchToInsertedModules(insertedModule, *handle);
}
- ALOGV("%s() added new patch handle %d halHandle %d", __func__, *handle, halHandle);
} else {
newPatch.clearConnections(this);
}
@@ -448,18 +454,6 @@
*mPlayback.handlePtr() = AUDIO_PATCH_HANDLE_NONE;
}
- // use a pseudo LCM between input and output framecount
- size_t playbackFrameCount = mPlayback.thread()->frameCount();
- int playbackShift = __builtin_ctz(playbackFrameCount);
- size_t recordFrameCount = mRecord.thread()->frameCount();
- int shift = __builtin_ctz(recordFrameCount);
- if (playbackShift < shift) {
- shift = playbackShift;
- }
- size_t frameCount = (playbackFrameCount * recordFrameCount) >> shift;
- ALOGV("%s() playframeCount %zu recordFrameCount %zu frameCount %zu",
- __func__, playbackFrameCount, recordFrameCount, frameCount);
-
// create a special record track to capture from record thread
uint32_t channelCount = mPlayback.thread()->channelCount();
audio_channel_mask_t inChannelMask = audio_channel_in_mask_from_count(channelCount);
@@ -503,7 +497,17 @@
}
sp<RecordThread::PatchRecord> tempRecordTrack;
- if ((inputFlags & AUDIO_INPUT_FLAG_DIRECT) && (outputFlags & AUDIO_OUTPUT_FLAG_DIRECT)) {
+ const bool usePassthruPatchRecord =
+ (inputFlags & AUDIO_INPUT_FLAG_DIRECT) && (outputFlags & AUDIO_OUTPUT_FLAG_DIRECT);
+ const size_t playbackFrameCount = mPlayback.thread()->frameCount();
+ const size_t recordFrameCount = mRecord.thread()->frameCount();
+ size_t frameCount = 0;
+ if (usePassthruPatchRecord) {
+ // PassthruPatchRecord producesBufferOnDemand, so use
+ // maximum of playback and record thread framecounts
+ frameCount = std::max(playbackFrameCount, recordFrameCount);
+ ALOGV("%s() playframeCount %zu recordFrameCount %zu frameCount %zu",
+ __func__, playbackFrameCount, recordFrameCount, frameCount);
tempRecordTrack = new RecordThread::PassthruPatchRecord(
mRecord.thread().get(),
sampleRate,
@@ -512,6 +516,16 @@
frameCount,
inputFlags);
} else {
+ // use a pseudo LCM between input and output framecount
+ int playbackShift = __builtin_ctz(playbackFrameCount);
+ int shift = __builtin_ctz(recordFrameCount);
+ if (playbackShift < shift) {
+ shift = playbackShift;
+ }
+ frameCount = (playbackFrameCount * recordFrameCount) >> shift;
+ ALOGV("%s() playframeCount %zu recordFrameCount %zu frameCount %zu",
+ __func__, playbackFrameCount, recordFrameCount, frameCount);
+
tempRecordTrack = new RecordThread::PatchRecord(
mRecord.thread().get(),
sampleRate,
@@ -545,8 +559,14 @@
}
// tie playback and record tracks together
- mRecord.setTrackAndPeer(tempRecordTrack, tempPatchTrack);
- mPlayback.setTrackAndPeer(tempPatchTrack, tempRecordTrack);
+ // In the case of PassthruPatchRecord no I/O activity happens on RecordThread,
+ // everything is driven from PlaybackThread. Thus AudioBufferProvider methods
+ // of PassthruPatchRecord can only be called if the corresponding PatchTrack
+ // is alive. There is no need to hold a reference, and there is no need
+ // to clear it. In fact, since playback stopping is asynchronous, there is
+ // no proper time when clearing could be done.
+ mRecord.setTrackAndPeer(tempRecordTrack, tempPatchTrack, !usePassthruPatchRecord);
+ mPlayback.setTrackAndPeer(tempPatchTrack, tempRecordTrack, true /*holdReference*/);
// start capture and playback
mRecord.track()->start(AudioSystem::SYNC_EVENT_NONE, AUDIO_SESSION_NONE);
@@ -628,8 +648,21 @@
String8 AudioFlinger::PatchPanel::Patch::dump(audio_patch_handle_t myHandle) const
{
// TODO: Consider table dump form for patches, just like tracks.
- String8 result = String8::format("Patch %d: thread %p => thread %p",
- myHandle, mRecord.const_thread().get(), mPlayback.const_thread().get());
+ String8 result = String8::format("Patch %d: %s (thread %p => thread %p)",
+ myHandle, isSoftware() ? "Software bridge between" : "No software bridge",
+ mRecord.const_thread().get(), mPlayback.const_thread().get());
+
+ bool hasSinkDevice =
+ mAudioPatch.num_sinks > 0 && mAudioPatch.sinks[0].type == AUDIO_PORT_TYPE_DEVICE;
+ bool hasSourceDevice =
+ mAudioPatch.num_sources > 0 && mAudioPatch.sources[0].type == AUDIO_PORT_TYPE_DEVICE;
+ result.appendFormat(" thread %p %s (%d) first device type %08x", mThread.unsafe_get(),
+ hasSinkDevice ? "num sinks" :
+ (hasSourceDevice ? "num sources" : "no devices"),
+ hasSinkDevice ? mAudioPatch.num_sinks :
+ (hasSourceDevice ? mAudioPatch.num_sources : 0),
+ hasSinkDevice ? mAudioPatch.sinks[0].ext.device.type :
+ (hasSourceDevice ? mAudioPatch.sources[0].ext.device.type : 0));
// add latency if it exists
double latencyMs;
@@ -705,11 +738,16 @@
status = BAD_VALUE;
}
- mPatches.erase(iter);
- removeSoftwarePatchFromInsertedModules(handle);
+ erasePatch(handle);
return status;
}
+void AudioFlinger::PatchPanel::erasePatch(audio_patch_handle_t handle) {
+ mPatches.erase(handle);
+ removeSoftwarePatchFromInsertedModules(handle);
+ mAudioFlinger.mDeviceEffectManager.releaseAudioPatch(handle);
+}
+
/* List connected audio ports and they attributes */
status_t AudioFlinger::PatchPanel::listAudioPatches(unsigned int *num_patches __unused,
struct audio_patch *patches __unused)
@@ -793,16 +831,13 @@
String8 patchPanelDump;
const char *indent = " ";
- // Only dump software patches.
bool headerPrinted = false;
for (const auto& iter : mPatches) {
- if (iter.second.isSoftware()) {
- if (!headerPrinted) {
- patchPanelDump += "\nSoftware patches:\n";
- headerPrinted = true;
- }
- patchPanelDump.appendFormat("%s%s\n", indent, iter.second.dump(iter.first).string());
+ if (!headerPrinted) {
+ patchPanelDump += "\nPatches:\n";
+ headerPrinted = true;
}
+ patchPanelDump.appendFormat("%s%s\n", indent, iter.second.dump(iter.first).string());
}
headerPrinted = false;
diff --git a/services/audioflinger/PatchPanel.h b/services/audioflinger/PatchPanel.h
index 181e27c..89d4eb1 100644
--- a/services/audioflinger/PatchPanel.h
+++ b/services/audioflinger/PatchPanel.h
@@ -76,13 +76,18 @@
void dump(int fd) const;
-private:
template<typename ThreadType, typename TrackType>
- class Endpoint {
+ class Endpoint final {
public:
Endpoint() = default;
Endpoint(const Endpoint&) = delete;
- Endpoint& operator=(const Endpoint&) = delete;
+ Endpoint& operator=(const Endpoint& other) noexcept {
+ mThread = other.mThread;
+ mCloseThread = other.mCloseThread;
+ mHandle = other.mHandle;
+ mTrack = other.mTrack;
+ return *this;
+ }
Endpoint(Endpoint&& other) noexcept { swap(other); }
Endpoint& operator=(Endpoint&& other) noexcept {
swap(other);
@@ -98,8 +103,8 @@
return trackOrNull->initCheck();
}
audio_patch_handle_t handle() const { return mHandle; }
- sp<ThreadType> thread() { return mThread; }
- sp<TrackType> track() { return mTrack; }
+ sp<ThreadType> thread() const { return mThread; }
+ sp<TrackType> track() const { return mTrack; }
sp<const ThreadType> const_thread() const { return mThread; }
sp<const TrackType> const_track() const { return mTrack; }
@@ -123,18 +128,20 @@
mCloseThread = closeThread;
}
template <typename T>
- void setTrackAndPeer(const sp<TrackType>& track, const sp<T> &peer) {
+ void setTrackAndPeer(const sp<TrackType>& track, const sp<T> &peer, bool holdReference) {
mTrack = track;
mThread->addPatchTrack(mTrack);
- mTrack->setPeerProxy(peer, true /* holdReference */);
+ mTrack->setPeerProxy(peer, holdReference);
+ mClearPeerProxy = holdReference;
}
- void clearTrackPeer() { if (mTrack) mTrack->clearPeerProxy(); }
+ void clearTrackPeer() { if (mClearPeerProxy && mTrack) mTrack->clearPeerProxy(); }
void stopTrack() { if (mTrack) mTrack->stop(); }
void swap(Endpoint &other) noexcept {
using std::swap;
swap(mThread, other.mThread);
swap(mCloseThread, other.mCloseThread);
+ swap(mClearPeerProxy, other.mClearPeerProxy);
swap(mHandle, other.mHandle);
swap(mTrack, other.mTrack);
}
@@ -146,18 +153,41 @@
private:
sp<ThreadType> mThread;
bool mCloseThread = true;
+ bool mClearPeerProxy = true;
audio_patch_handle_t mHandle = AUDIO_PATCH_HANDLE_NONE;
sp<TrackType> mTrack;
};
- class Patch {
+ class Patch final {
public:
explicit Patch(const struct audio_patch &patch) : mAudioPatch(patch) {}
+ Patch() = default;
~Patch();
- Patch(const Patch&) = delete;
- Patch(Patch&&) = default;
- Patch& operator=(const Patch&) = delete;
- Patch& operator=(Patch&&) = default;
+ Patch(const Patch& other) noexcept {
+ mAudioPatch = other.mAudioPatch;
+ mHalHandle = other.mHalHandle;
+ mPlayback = other.mPlayback;
+ mRecord = other.mRecord;
+ mThread = other.mThread;
+ }
+ Patch(Patch&& other) noexcept { swap(other); }
+ Patch& operator=(Patch&& other) noexcept {
+ swap(other);
+ return *this;
+ }
+
+ void swap(Patch &other) noexcept {
+ using std::swap;
+ swap(mAudioPatch, other.mAudioPatch);
+ swap(mHalHandle, other.mHalHandle);
+ swap(mPlayback, other.mPlayback);
+ swap(mRecord, other.mRecord);
+ swap(mThread, other.mThread);
+ }
+
+ friend void swap(Patch &a, Patch &b) noexcept {
+ a.swap(b);
+ }
status_t createConnections(PatchPanel *panel);
void clearConnections(PatchPanel *panel);
@@ -165,6 +195,9 @@
return mRecord.handle() != AUDIO_PATCH_HANDLE_NONE ||
mPlayback.handle() != AUDIO_PATCH_HANDLE_NONE; }
+ void setThread(sp<ThreadBase> thread) { mThread = thread; }
+ wp<ThreadBase> thread() const { return mThread; }
+
// returns the latency of the patch (from record to playback).
status_t getLatencyMs(double *latencyMs) const;
@@ -182,13 +215,20 @@
Endpoint<PlaybackThread, PlaybackThread::PatchTrack> mPlayback;
// connects source device to record thread input
Endpoint<RecordThread, RecordThread::PatchRecord> mRecord;
+
+ wp<ThreadBase> mThread;
};
+ // Call with AudioFlinger mLock held
+ std::map<audio_patch_handle_t, Patch>& patches_l() { return mPatches; }
+
+private:
AudioHwDevice* findAudioHwDeviceByModule(audio_module_handle_t module);
sp<DeviceHalInterface> findHwDeviceByModule(audio_module_handle_t module);
void addSoftwarePatchToInsertedModules(
audio_module_handle_t module, audio_patch_handle_t handle);
void removeSoftwarePatchFromInsertedModules(audio_patch_handle_t handle);
+ void erasePatch(audio_patch_handle_t handle);
AudioFlinger &mAudioFlinger;
std::map<audio_patch_handle_t, Patch> mPatches;
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index e70c776..ad09680 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -24,6 +24,7 @@
#include <math.h>
#include <fcntl.h>
#include <memory>
+#include <sstream>
#include <string>
#include <linux/futex.h>
#include <sys/stat.h>
@@ -212,6 +213,28 @@
// ----------------------------------------------------------------------------
+// TODO: move all toString helpers to audio.h
+// under #ifdef __cplusplus #endif
+static std::string patchSinksToString(const struct audio_patch *patch)
+{
+ std::stringstream ss;
+ for (size_t i = 0; i < patch->num_sinks; ++i) {
+ ss << "(" << toString(patch->sinks[i].ext.device.type)
+ << ", " << patch->sinks[i].ext.device.address << ")";
+ }
+ return ss.str();
+}
+
+static std::string patchSourcesToString(const struct audio_patch *patch)
+{
+ std::stringstream ss;
+ for (size_t i = 0; i < patch->num_sources; ++i) {
+ ss << "(" << toString(patch->sources[i].ext.device.type)
+ << ", " << patch->sources[i].ext.device.address << ")";
+ }
+ return ss.str();
+}
+
static pthread_once_t sFastTrackMultiplierOnce = PTHREAD_ONCE_INIT;
static void sFastTrackMultiplierInit()
@@ -466,6 +489,7 @@
: Thread(false /*canCallJava*/),
mType(type),
mAudioFlinger(audioFlinger),
+ mMetricsId(std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD) + std::to_string(id)),
// mSampleRate, mFrameCount, mChannelMask, mChannelCount, mFrameSize, mFormat, mBufferSize
// are set by PlaybackThread::readOutputParameters_l() or
// RecordThread::readInputParameters_l()
@@ -477,6 +501,13 @@
mSystemReady(systemReady),
mSignalPending(false)
{
+ mediametrics::LogItem(mMetricsId)
+ .setPid(getpid())
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_CTOR)
+ .set(AMEDIAMETRICS_PROP_TYPE, threadTypeToString(type))
+ .set(AMEDIAMETRICS_PROP_THREADID, id)
+ .record();
+
memset(&mPatch, 0, sizeof(struct audio_patch));
}
@@ -493,6 +524,10 @@
}
sendStatistics(true /* force */);
+
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_DTOR)
+ .record();
}
status_t AudioFlinger::ThreadBase::readyToRun()
@@ -1146,18 +1181,13 @@
}
}
-void AudioFlinger::ThreadBase::checkSuspendOnEffectEnabled(const sp<EffectModule>& effect,
- bool enabled,
- audio_session_t sessionId)
-{
- Mutex::Autolock _l(mLock);
- checkSuspendOnEffectEnabled_l(effect, enabled, sessionId);
-}
+void AudioFlinger::ThreadBase::checkSuspendOnEffectEnabled(bool enabled,
+ audio_session_t sessionId,
+ bool threadLocked) {
+ if (!threadLocked) {
+ mLock.lock();
+ }
-void AudioFlinger::ThreadBase::checkSuspendOnEffectEnabled_l(const sp<EffectModule>& effect,
- bool enabled,
- audio_session_t sessionId)
-{
if (mType != RECORD) {
// suspend all effects in AUDIO_SESSION_OUTPUT_MIX when enabling any effect on
// another session. This gives the priority to well behaved effect control panels
@@ -1169,9 +1199,8 @@
}
}
- sp<EffectChain> chain = getEffectChain_l(sessionId);
- if (chain != 0) {
- chain->checkSuspendOnEffectEnabled(effect, enabled);
+ if (!threadLocked) {
+ mLock.unlock();
}
}
@@ -1379,7 +1408,7 @@
if (effect == 0) {
effectId = mAudioFlinger->nextUniqueId(AUDIO_UNIQUE_ID_USE_EFFECT);
// create a new effect module if none present in the chain
- lStatus = chain->createEffect_l(effect, this, desc, effectId, sessionId, pinned);
+ lStatus = chain->createEffect_l(effect, desc, effectId, sessionId, pinned);
if (lStatus != NO_ERROR) {
goto Exit;
}
@@ -1425,9 +1454,12 @@
sp<EffectModule> effect;
{
Mutex::Autolock _l(mLock);
-
- effect = handle->effect().promote();
- if (effect == 0) {
+ sp<EffectBase> effectBase = handle->effect().promote();
+ if (effectBase == nullptr) {
+ return;
+ }
+ effect = effectBase->asEffectModule();
+ if (effect == nullptr) {
return;
}
// restore suspended effects if the disconnected handle was enabled and the last one.
@@ -1439,11 +1471,34 @@
if (remove) {
mAudioFlinger->updateOrphanEffectChains(effect);
if (handle->enabled()) {
- checkSuspendOnEffectEnabled(effect, false, effect->sessionId());
+ effect->checkSuspendOnEffectEnabled(false, false /*threadLocked*/);
}
}
}
+void AudioFlinger::ThreadBase::onEffectEnable(const sp<EffectModule>& effect) {
+ if (mType == OFFLOAD || mType == MMAP) {
+ Mutex::Autolock _l(mLock);
+ broadcast_l();
+ }
+ if (!effect->isOffloadable()) {
+ if (mType == ThreadBase::OFFLOAD) {
+ PlaybackThread *t = (PlaybackThread *)this;
+ t->invalidateTracks(AUDIO_STREAM_MUSIC);
+ }
+ if (effect->sessionId() == AUDIO_SESSION_OUTPUT_MIX) {
+ mAudioFlinger->onNonOffloadableGlobalEffectEnable();
+ }
+ }
+}
+
+void AudioFlinger::ThreadBase::onEffectDisable() {
+ if (mType == OFFLOAD || mType == MMAP) {
+ Mutex::Autolock _l(mLock);
+ broadcast_l();
+ }
+}
+
sp<AudioFlinger::EffectModule> AudioFlinger::ThreadBase::getEffect(audio_session_t sessionId,
int effectId)
{
@@ -1519,7 +1574,7 @@
detachAuxEffect_l(effect->id());
}
- sp<EffectChain> chain = effect->chain().promote();
+ sp<EffectChain> chain = effect->callback()->chain().promote();
if (chain != 0) {
// remove effect chain if removing last effect
if (chain->removeEffect_l(effect, release) == 0) {
@@ -1725,7 +1780,7 @@
mLastRecordedTimestampVerifierN = mTimestampVerifier.getN();
mLastRecordedTimeNs = timeNs;
- std::unique_ptr<MediaAnalyticsItem> item(MediaAnalyticsItem::create("audiothread"));
+ std::unique_ptr<mediametrics::Item> item(mediametrics::Item::create("audiothread"));
#define MM_PREFIX "android.media.audiothread." // avoid cut-n-paste errors.
@@ -2774,6 +2829,30 @@
mAudioFlinger->moveEffectChain_l(effectChains[i]->sessionId(),
this/* srcThread */, this/* dstThread */);
}
+
+ audio_output_flags_t flags = mOutput != nullptr ? mOutput->flags : AUDIO_OUTPUT_FLAG_NONE;
+ mediametrics::LogItem item(mMetricsId);
+ item.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_READPARAMETERS)
+ .set(AMEDIAMETRICS_PROP_ENCODING, formatToString(mFormat).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)mNormalFrameCount)
+ .set(AMEDIAMETRICS_PROP_FLAGS, toString(flags).c_str())
+ .set(AMEDIAMETRICS_PROP_PREFIX_HAPTIC AMEDIAMETRICS_PROP_CHANNELMASK,
+ (int32_t)mHapticChannelMask)
+ .set(AMEDIAMETRICS_PROP_PREFIX_HAPTIC AMEDIAMETRICS_PROP_CHANNELCOUNT,
+ (int32_t)mHapticChannelCount)
+ .set(AMEDIAMETRICS_PROP_PREFIX_HAL AMEDIAMETRICS_PROP_ENCODING,
+ formatToString(mHALFormat).c_str())
+ .set(AMEDIAMETRICS_PROP_PREFIX_HAL AMEDIAMETRICS_PROP_FRAMECOUNT,
+ (int32_t)mFrameCount) // sic - added HAL
+ ;
+ uint32_t latencyMs;
+ if (mOutput->stream->getLatency(&latencyMs) == NO_ERROR) {
+ item.set(AMEDIAMETRICS_PROP_PREFIX_HAL AMEDIAMETRICS_PROP_LATENCYMS, (double)latencyMs);
+ }
+ item.record();
}
void AudioFlinger::PlaybackThread::updateMetadata_l()
@@ -3850,6 +3929,11 @@
const int32_t throttleMs = (int32_t)mHalfBufferMs - deltaMs;
if ((signed)mHalfBufferMs >= throttleMs && throttleMs > 0) {
+ mediametrics::LogItem(mMetricsId)
+ // ms units always double
+ .set(AMEDIAMETRICS_PROP_THROTTLEMS, (double)throttleMs)
+ .record();
+
usleep(throttleMs * 1000);
// notify of throttle start on verbose log
ALOGV_IF(mThreadThrottleEndMs == mThreadThrottleTimeMs,
@@ -4122,6 +4206,11 @@
status = mOutput->stream->setParameters(param.toString());
*handle = AUDIO_PATCH_HANDLE_NONE;
}
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_CREATEAUDIOPATCH)
+ .set(AMEDIAMETRICS_PROP_OUTPUTDEVICES, patchSinksToString(patch).c_str())
+ .record();
+
if (configChanged) {
sendIoConfigEvent_l(AUDIO_OUTPUT_CONFIG_CHANGED);
}
@@ -4663,19 +4752,29 @@
// DeferredOperations handles statistics after setting mixerStatus.
class DeferredOperations {
public:
- DeferredOperations(mixer_state *mixerStatus)
- : mMixerStatus(mixerStatus) { }
+ DeferredOperations(mixer_state *mixerStatus, const std::string &metricsId)
+ : mMixerStatus(mixerStatus)
+ , mMetricsId(metricsId) {}
// 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.
- if (*mMixerStatus == MIXER_TRACKS_READY) {
+ if (*mMixerStatus == MIXER_TRACKS_READY && mUnderrunFrames.size() > 0) {
+ mediametrics::LogItem item(mMetricsId);
+
+ item.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_UNDERRUN);
for (const auto &underrun : mUnderrunFrames) {
underrun.first->mAudioTrackServerProxy->tallyUnderrunFrames(
underrun.second);
+
+ item.set(std::string("[" AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK)
+ + std::to_string(underrun.first->portId())
+ + "]" AMEDIAMETRICS_PROP_UNDERRUN,
+ (int32_t)underrun.second);
}
+ item.record();
}
}
@@ -4688,8 +4787,10 @@
private:
const mixer_state * const mMixerStatus;
+ const std::string& mMetricsId;
std::vector<std::pair<sp<Track>, size_t>> mUnderrunFrames;
- } deferredOperations(&mixerStatus); // implicit nested scope for variable capture
+ } deferredOperations(&mixerStatus, mMetricsId);
+ // implicit nested scope for variable capture
bool noFastHapticTrack = true;
for (size_t i=0 ; i<count ; i++) {
@@ -5167,11 +5268,20 @@
mixerStatus != MIXER_TRACKS_ENABLED) {
mixerStatus = MIXER_TRACKS_READY;
}
+
+ // Enable the next few lines to instrument a test for underrun log handling.
+ // TODO: Remove when we have a better way of testing the underrun log.
+#if 0
+ static int i;
+ if ((++i & 0xf) == 0) {
+ deferredOperations.tallyUnderrunFrames(track, 10 /* underrunFrames */);
+ }
+#endif
} else {
size_t underrunFrames = 0;
if (framesReady < desiredFrames && !track->isStopped() && !track->isPaused()) {
- ALOGV("track(%d) underrun, framesReady(%zu) < framesDesired(%zd)",
- trackId, framesReady, desiredFrames);
+ ALOGV("track(%d) underrun, track state %s framesReady(%zu) < framesDesired(%zd)",
+ trackId, track->getTrackStateAsString(), framesReady, desiredFrames);
underrunFrames = desiredFrames;
}
deferredOperations.tallyUnderrunFrames(track, underrunFrames);
@@ -5999,6 +6109,7 @@
mHwPaused = false;
mFlushPending = false;
mTimestampVerifier.discontinuity(); // DIRECT and OFFLOADED flush resets frame count.
+ mTimestamp.clear();
}
int64_t AudioFlinger::DirectOutputThread::computeWaitTimeNs_l() const {
@@ -8384,6 +8495,12 @@
mPatch = *patch;
}
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_CREATEAUDIOPATCH)
+ .set(AMEDIAMETRICS_PROP_INPUTDEVICES, patchSourcesToString(patch).c_str())
+ .set(AMEDIAMETRICS_PROP_SOURCE, toString(mAudioSource).c_str())
+ .record();
+
return status;
}
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index ef7eb6e..b0eb75d 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -271,6 +271,8 @@
// Called by AudioFlinger::frameCount(audio_io_handle_t output) and effects,
// and returns the [normal mix] buffer's frame count.
virtual size_t frameCount() const = 0;
+ virtual uint32_t latency_l() const { return 0; }
+ virtual void setVolumeForOutput_l(float left __unused, float right __unused) const {}
// Return's the HAL's frame count i.e. fast mixer buffer size.
size_t frameCountHAL() const { return mFrameCount; }
@@ -424,14 +426,9 @@
// check if some effects must be suspended/restored when an effect is enabled
// or disabled
- void checkSuspendOnEffectEnabled(const sp<EffectModule>& effect,
- bool enabled,
- audio_session_t sessionId =
- AUDIO_SESSION_OUTPUT_MIX);
- void checkSuspendOnEffectEnabled_l(const sp<EffectModule>& effect,
- bool enabled,
- audio_session_t sessionId =
- AUDIO_SESSION_OUTPUT_MIX);
+ void checkSuspendOnEffectEnabled(bool enabled,
+ audio_session_t sessionId,
+ bool threadLocked);
virtual status_t setSyncEvent(const sp<SyncEvent>& event) = 0;
virtual bool isValidSyncEvent(const sp<SyncEvent>& event) const = 0;
@@ -465,6 +462,9 @@
mutable Mutex mLock;
+ void onEffectEnable(const sp<EffectModule>& effect);
+ void onEffectDisable();
+
protected:
// entry describing an effect being suspended in mSuspendedSessions keyed vector
@@ -523,6 +523,7 @@
Condition mWaitWorkCV;
const sp<AudioFlinger> mAudioFlinger;
+ const std::string mMetricsId;
// updated by PlaybackThread::readOutputParameters_l() or
// RecordThread::readInputParameters_l()
@@ -814,7 +815,7 @@
// return estimated latency in milliseconds, as reported by HAL
uint32_t latency() const;
// same, but lock must already be held
- uint32_t latency_l() const;
+ uint32_t latency_l() const override;
// VolumeInterface
virtual void setMasterVolume(float value);
@@ -824,7 +825,7 @@
virtual void setStreamMute(audio_stream_type_t stream, bool muted);
virtual float streamVolume(audio_stream_type_t stream) const;
- void setVolumeForOutput_l(float left, float right) const;
+ void setVolumeForOutput_l(float left, float right) const override;
sp<Track> createTrack_l(
const sp<AudioFlinger::Client>& client,
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index 91dbfa4..e39b944 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -69,7 +69,8 @@
bool isOut,
alloc_type alloc = ALLOC_CBLK,
track_type type = TYPE_DEFAULT,
- audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE);
+ audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE,
+ std::string metricsId = {});
virtual ~TrackBase();
virtual status_t initCheck() const;
@@ -94,7 +95,14 @@
bool isPatchTrack() const { return (mType == TYPE_PATCH); }
bool isExternalTrack() const { return !isOutputTrack() && !isPatchTrack(); }
- virtual void invalidate() { mIsInvalid = true; }
+ virtual void invalidate() {
+ if (mIsInvalid) return;
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT,
+ AMEDIAMETRICS_PROP_EVENT_VALUE_INVALIDATE)
+ .record();
+ mIsInvalid = true;
+ }
bool isInvalid() const { return mIsInvalid; }
void terminate() { mTerminated = true; }
@@ -353,6 +361,14 @@
audio_port_handle_t mPortId; // unique ID for this track used by audio policy
bool mIsInvalid; // non-resettable latch, set by invalidate()
+ // It typically takes 5 threadloop mix iterations for latency to stabilize.
+ static inline constexpr int32_t LOG_START_COUNTDOWN = 8;
+ int32_t mLogStartCountdown = 0;
+ int64_t mLogStartTimeNs = 0;
+ int64_t mLogStartFrames = 0;
+
+ const std::string mMetricsId;
+
bool mServerLatencySupported = false;
std::atomic<bool> mServerLatencyFromTrack{}; // latency from track or server timestamp.
std::atomic<double> mServerLatencyMs{}; // last latency pushed from server thread.
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 2437202..23d8329 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -80,7 +80,8 @@
bool isOut,
alloc_type alloc,
track_type type,
- audio_port_handle_t portId)
+ audio_port_handle_t portId,
+ std::string metricsId)
: RefBase(),
mThread(thread),
mClient(client),
@@ -105,6 +106,7 @@
mThreadIoHandle(thread ? thread->id() : AUDIO_IO_HANDLE_NONE),
mPortId(portId),
mIsInvalid(false),
+ mMetricsId(std::move(metricsId)),
mCreatorPid(creatorPid)
{
const uid_t callingUid = IPCThreadState::self()->getCallingUid();
@@ -519,7 +521,9 @@
(sharedBuffer != 0) ? sharedBuffer->size() : bufferSize,
sessionId, creatorPid, uid, true /*isOut*/,
(type == TYPE_PATCH) ? ( buffer == NULL ? ALLOC_LOCAL : ALLOC_NONE) : ALLOC_CBLK,
- type, portId),
+ type,
+ portId,
+ std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK) + std::to_string(portId)),
mFillingUpStatus(FS_INVALID),
// mRetryCount initialized later when needed
mSharedBuffer(sharedBuffer),
@@ -596,6 +600,14 @@
mExternalVibration = new os::ExternalVibration(
mUid, "" /* pkg */, mAttr, mAudioVibrationController);
}
+
+ // Once this item is logged by the server, the client can add properties.
+ mediametrics::LogItem(mMetricsId)
+ .setPid(creatorPid)
+ .setUid(uid)
+ .set(AMEDIAMETRICS_PROP_EVENT,
+ AMEDIAMETRICS_PROP_PREFIX_SERVER AMEDIAMETRICS_PROP_EVENT_VALUE_CTOR)
+ .record();
}
AudioFlinger::PlaybackThread::Track::~Track()
@@ -966,6 +978,14 @@
}
}
+ // Audio timing metrics are computed a few mix cycles after starting.
+ {
+ mLogStartCountdown = LOG_START_COUNTDOWN;
+ mLogStartTimeNs = systemTime();
+ mLogStartFrames = mAudioTrackServerProxy->getTimestamp()
+ .mPosition[ExtendedTimestamp::LOCATION_SERVER];
+ }
+
if (status == NO_ERROR || status == ALREADY_EXISTS) {
// for streaming tracks, remove the buffer read stop limit.
mAudioTrackServerProxy->start();
@@ -1497,6 +1517,28 @@
mServerLatencyFromTrack.store(useTrackTimestamp);
mServerLatencyMs.store(latencyMs);
+
+ if (mLogStartCountdown > 0) {
+ if (--mLogStartCountdown == 0) {
+ // startup is the difference in times for the current timestamp and our start
+ double startUpMs =
+ (local.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] - mLogStartTimeNs) * 1e-6;
+ // adjust for frames played.
+ startUpMs -= (local.mPosition[ExtendedTimestamp::LOCATION_SERVER] - mLogStartFrames)
+ * 1e3 / mSampleRate;
+ ALOGV("%s: logging localTime:%lld, startTime:%lld"
+ " localPosition:%lld, startPosition:%lld",
+ __func__,
+ (long long)local.mTimeNs[ExtendedTimestamp::LOCATION_SERVER],
+ (long long)mLogStartTimeNs,
+ (long long)local.mPosition[ExtendedTimestamp::LOCATION_SERVER],
+ (long long)mLogStartFrames);
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_LATENCYMS, latencyMs)
+ .set(AMEDIAMETRICS_PROP_STARTUPMS, startUpMs)
+ .record();
+ }
+ }
}
binder::Status AudioFlinger::PlaybackThread::Track::AudioVibrationController::mute(
@@ -2071,7 +2113,8 @@
(type == TYPE_DEFAULT) ?
((flags & AUDIO_INPUT_FLAG_FAST) ? ALLOC_PIPE : ALLOC_CBLK) :
((buffer == NULL) ? ALLOC_LOCAL : ALLOC_NONE),
- type, portId),
+ type, portId,
+ std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_RECORD) + std::to_string(portId)),
mOverflow(false),
mFramesToDrop(0),
mResamplerBufferProvider(NULL), // initialize in case of early constructor exit
@@ -2117,6 +2160,13 @@
+ "_" + std::to_string(mId)
+ "_R");
#endif
+
+ // Once this item is logged by the server, the client can add properties.
+ mediametrics::LogItem(mMetricsId)
+ .setPid(creatorPid)
+ .setUid(uid)
+ .set(AMEDIAMETRICS_PROP_EVENT, "server." AMEDIAMETRICS_PROP_EVENT_VALUE_CTOR)
+ .record();
}
AudioFlinger::RecordThread::RecordTrack::~RecordTrack()
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index 00f14e1..4d53be4 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -192,6 +192,10 @@
// return the enabled output devices for the given stream type
virtual audio_devices_t getDevicesForStream(audio_stream_type_t stream) = 0;
+ // retrieves the list of enabled output devices for the given audio attributes
+ virtual status_t getDevicesForAttributes(const audio_attributes_t &attr,
+ AudioDeviceTypeAddrVector *devices) = 0;
+
// Audio effect management
virtual audio_io_handle_t getOutputForEffect(const effect_descriptor_t *desc) = 0;
virtual status_t registerEffect(const effect_descriptor_t *desc,
@@ -246,6 +250,10 @@
= 0;
virtual status_t removeUidDeviceAffinities(uid_t uid) = 0;
+ virtual status_t setUserIdDeviceAffinities(int userId,
+ const Vector<AudioDeviceTypeAddr>& devices) = 0;
+ virtual status_t removeUserIdDeviceAffinities(int userId) = 0;
+
virtual status_t startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,
audio_port_handle_t *portId,
@@ -403,6 +411,12 @@
std::vector<effect_descriptor_t> effects,
audio_patch_handle_t patchHandle,
audio_source_t source) = 0;
+
+ // Used to notify the sound trigger module that an audio capture is about to
+ // take place. This should typically result in any active recognition
+ // sessions to be preempted on modules that do not support sound trigger
+ // recognition concurrently with audio capture.
+ virtual void setSoundTriggerCaptureState(bool active) = 0;
};
extern "C" AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface);
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPatch.h b/services/audiopolicy/common/managerdefinitions/include/AudioPatch.h
index 0843fea..a5de655 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPatch.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPatch.h
@@ -31,12 +31,24 @@
public:
AudioPatch(const struct audio_patch *patch, uid_t uid);
+ audio_patch_handle_t getHandle() const { return mHandle; }
+
+ audio_patch_handle_t getAfHandle() const { return mAfPatchHandle; }
+
+ void setAfHandle(audio_patch_handle_t afHandle) { mAfPatchHandle = afHandle; }
+
+ uid_t getUid() const { return mUid; }
+
+ void setUid(uid_t uid) { mUid = uid; }
+
void dump(String8 *dst, int spaces, int index) const;
- audio_patch_handle_t mHandle;
struct audio_patch mPatch;
+
+private:
+ const audio_patch_handle_t mHandle;
uid_t mUid;
- audio_patch_handle_t mAfPatchHandle;
+ audio_patch_handle_t mAfPatchHandle = AUDIO_PATCH_HANDLE_NONE;
};
class AudioPatchCollection : public DefaultKeyedVector<audio_patch_handle_t, sp<AudioPatch> >
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
index fc79ab1..a757551 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
@@ -105,12 +105,27 @@
status_t removeUidDeviceAffinities(uid_t uid);
status_t getDevicesForUid(uid_t uid, Vector<AudioDeviceTypeAddr>& devices) const;
+ /**
+ * Updates the mix rules in order to make streams associated with the given user
+ * be routed to the given audio devices.
+ * @param userId the userId for which the device affinity is set
+ * @param devices the vector of devices that this userId may be routed to. A typical
+ * use is to pass the devices associated with a given zone in a multi-zone setup.
+ * @return NO_ERROR if the update was successful, INVALID_OPERATION otherwise.
+ * An example of failure is when there are already rules in place to restrict
+ * a mix to the given userId (i.e. when a MATCH_USERID rule was set for it).
+ */
+ status_t setUserIdDeviceAffinities(int userId, const Vector<AudioDeviceTypeAddr>& devices);
+ status_t removeUserIdDeviceAffinities(int userId);
+ status_t getDevicesForUserId(int userId, Vector<AudioDeviceTypeAddr>& devices) const;
+
void dump(String8 *dst) const;
private:
enum class MixMatchStatus { MATCH, NO_MATCH, INVALID_MIX };
MixMatchStatus mixMatch(const AudioMix* mix, size_t mixIndex,
- const audio_attributes_t& attributes, uid_t uid);
+ const audio_attributes_t& attributes,
+ uid_t uid);
};
} // namespace android
diff --git a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
index 0d05a63..0c5d1d0 100644
--- a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
@@ -183,13 +183,17 @@
{
public:
SourceClientDescriptor(audio_port_handle_t portId, uid_t uid, audio_attributes_t attributes,
- const sp<AudioPatch>& patchDesc, const sp<DeviceDescriptor>& srcDevice,
+ const struct audio_port_config &config,
+ const sp<DeviceDescriptor>& srcDevice,
audio_stream_type_t stream, product_strategy_t strategy,
VolumeSource volumeSource);
+
~SourceClientDescriptor() override = default;
- sp<AudioPatch> patchDesc() const { return mPatchDesc; }
- sp<DeviceDescriptor> srcDevice() const { return mSrcDevice; };
+ audio_patch_handle_t getPatchHandle() const { return mPatchHandle; }
+ void setPatchHandle(audio_patch_handle_t patchHandle) { mPatchHandle = patchHandle; }
+
+ sp<DeviceDescriptor> srcDevice() const { return mSrcDevice; }
wp<SwAudioOutputDescriptor> swOutput() const { return mSwOutput; }
void setSwOutput(const sp<SwAudioOutputDescriptor>& swOutput);
wp<HwAudioOutputDescriptor> hwOutput() const { return mHwOutput; }
@@ -199,7 +203,7 @@
void dump(String8 *dst, int spaces, int index) const override;
private:
- const sp<AudioPatch> mPatchDesc;
+ audio_patch_handle_t mPatchHandle = AUDIO_PATCH_HANDLE_NONE;
const sp<DeviceDescriptor> mSrcDevice;
wp<SwAudioOutputDescriptor> mSwOutput;
wp<HwAudioOutputDescriptor> mHwOutput;
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPatch.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPatch.cpp
index bf0cc94..d79110a 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPatch.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPatch.cpp
@@ -26,10 +26,9 @@
namespace android {
AudioPatch::AudioPatch(const struct audio_patch *patch, uid_t uid) :
- mHandle(HandleGenerator<audio_patch_handle_t>::getNextHandle()),
mPatch(*patch),
- mUid(uid),
- mAfPatchHandle(AUDIO_PATCH_HANDLE_NONE)
+ mHandle(HandleGenerator<audio_patch_handle_t>::getNextHandle()),
+ mUid(uid)
{
}
@@ -68,7 +67,7 @@
add(handle, patch);
ALOGV("addAudioPatch() handle %d af handle %d num_sources %d num_sinks %d source handle %d"
"sink handle %d",
- handle, patch->mAfPatchHandle, patch->mPatch.num_sources, patch->mPatch.num_sinks,
+ handle, patch->getAfHandle(), patch->mPatch.num_sources, patch->mPatch.num_sinks,
patch->mPatch.sources[0].id, patch->mPatch.sinks[0].id);
return NO_ERROR;
}
@@ -81,7 +80,7 @@
ALOGW("removeAudioPatch() patch %d not in", handle);
return ALREADY_EXISTS;
}
- ALOGV("removeAudioPatch() handle %d af handle %d", handle, valueAt(index)->mAfPatchHandle);
+ ALOGV("removeAudioPatch() handle %d af handle %d", handle, valueAt(index)->getAfHandle());
removeItemsAt(index);
return NO_ERROR;
}
@@ -123,7 +122,7 @@
}
if (patchesWritten < patchesMax) {
patches[patchesWritten] = patch->mPatch;
- patches[patchesWritten++].id = patch->mHandle;
+ patches[patchesWritten++].id = patch->getHandle();
}
(*num_patches)++;
ALOGV("listAudioPatches() patch %zu num_sources %d num_sinks %d",
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
index 20c0a24..11660f0 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
@@ -60,6 +60,9 @@
case RULE_MATCH_UID:
ruleValue = std::to_string(criterion.mValue.mUid);
break;
+ case RULE_MATCH_USERID:
+ ruleValue = std::to_string(criterion.mValue.mUserId);
+ break;
default:
unknownRule = true;
}
@@ -197,7 +200,9 @@
ALOGV("%s: Mix %zu ignored as secondaryOutput because not opened yet", __func__, i);
} else {
ALOGV("%s: Add a secondary desc %zu", __func__, i);
- secondaryDescs->push_back(policyDesc);
+ if (secondaryDescs != nullptr) {
+ secondaryDescs->push_back(policyDesc);
+ }
}
}
}
@@ -221,10 +226,14 @@
}
if (!(attributes.usage == AUDIO_USAGE_UNKNOWN ||
attributes.usage == AUDIO_USAGE_MEDIA ||
- attributes.usage == AUDIO_USAGE_GAME)) {
+ attributes.usage == AUDIO_USAGE_GAME ||
+ attributes.usage == AUDIO_USAGE_VOICE_COMMUNICATION)) {
return MixMatchStatus::NO_MATCH;
}
}
+
+ int userId = (int) multiuser_get_user_id(uid);
+
// TODO if adding more player rules (currently only 2), make rule handling "generic"
// as there is no difference in the treatment of usage- or uid-based rules
bool hasUsageMatchRules = false;
@@ -237,6 +246,12 @@
bool uidMatchFound = false;
bool uidExclusionFound = false;
+ bool hasUserIdExcludeRules = false;
+ bool userIdExclusionFound = false;
+ bool hasUserIdMatchRules = false;
+ bool userIdMatchFound = false;
+
+
bool hasAddrMatch = false;
// iterate over all mix criteria to list what rules this mix contains
@@ -288,6 +303,24 @@
uidExclusionFound = true;
}
break;
+ case RULE_MATCH_USERID:
+ ALOGV("\tmix has RULE_MATCH_USERID for userId %d",
+ mix->mCriteria[j].mValue.mUserId);
+ hasUserIdMatchRules = true;
+ if (mix->mCriteria[j].mValue.mUserId == userId) {
+ // found one userId match against all allowed userIds
+ userIdMatchFound = true;
+ }
+ break;
+ case RULE_EXCLUDE_USERID:
+ ALOGV("\tmix has RULE_EXCLUDE_USERID for userId %d",
+ mix->mCriteria[j].mValue.mUserId);
+ hasUserIdExcludeRules = true;
+ if (mix->mCriteria[j].mValue.mUserId == userId) {
+ // found this userId is to be excluded
+ userIdExclusionFound = true;
+ }
+ break;
default:
break;
}
@@ -304,20 +337,27 @@
" and RULE_EXCLUDE_UID in mix %zu", mixIndex);
return MixMatchStatus::INVALID_MIX;
}
-
- if ((hasUsageExcludeRules && usageExclusionFound)
- || (hasUidExcludeRules && uidExclusionFound)) {
- break; // stop iterating on criteria because an exclusion was found (will fail)
+ if (hasUserIdMatchRules && hasUserIdExcludeRules) {
+ ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_USERID"
+ " and RULE_EXCLUDE_USERID in mix %zu", mixIndex);
+ return MixMatchStatus::INVALID_MIX;
}
+ if ((hasUsageExcludeRules && usageExclusionFound)
+ || (hasUidExcludeRules && uidExclusionFound)
+ || (hasUserIdExcludeRules && userIdExclusionFound)) {
+ break; // stop iterating on criteria because an exclusion was found (will fail)
+ }
}//iterate on mix criteria
// determine if exiting on success (or implicit failure as desc is 0)
if (hasAddrMatch ||
!((hasUsageExcludeRules && usageExclusionFound) ||
+ (hasUserIdExcludeRules && userIdExclusionFound) ||
(hasUsageMatchRules && !usageMatchFound) ||
(hasUidExcludeRules && uidExclusionFound) ||
- (hasUidMatchRules && !uidMatchFound))) {
+ (hasUidMatchRules && !uidMatchFound)) ||
+ (hasUserIdMatchRules && !userIdMatchFound)) {
ALOGV("\tgetOutputForAttr will use mix %zu", mixIndex);
return MixMatchStatus::MATCH;
}
@@ -528,6 +568,109 @@
return NO_ERROR;
}
+status_t AudioPolicyMixCollection::setUserIdDeviceAffinities(int userId,
+ const Vector<AudioDeviceTypeAddr>& devices) {
+ // verify feasibility: for each player mix: if it already contains a
+ // "match userId" rule for this userId, return an error
+ // (adding a userId-device affinity would result in contradictory rules)
+ for (size_t i = 0; i < size(); i++) {
+ const AudioPolicyMix* mix = itemAt(i).get();
+ if (!mix->isDeviceAffinityCompatible()) {
+ continue;
+ }
+ if (mix->hasUserIdRule(true /*match*/, userId)) {
+ return INVALID_OPERATION;
+ }
+ }
+
+ // remove existing rules for this userId
+ removeUserIdDeviceAffinities(userId);
+
+ // for each player mix:
+ // IF device is not a target for the mix,
+ // AND it doesn't have a "match userId" rule
+ // THEN add a rule to exclude the userId
+ for (size_t i = 0; i < size(); i++) {
+ const AudioPolicyMix *mix = itemAt(i).get();
+ if (!mix->isDeviceAffinityCompatible()) {
+ continue;
+ }
+ // check if this mix goes to a device in the list of devices
+ bool deviceMatch = false;
+ const AudioDeviceTypeAddr mixDevice(mix->mDeviceType, mix->mDeviceAddress.string());
+ for (size_t j = 0; j < devices.size(); j++) {
+ if (mixDevice.equals(devices[j])) {
+ deviceMatch = true;
+ break;
+ }
+ }
+ if (!deviceMatch && !mix->hasMatchUserIdRule()) {
+ // this mix doesn't go to one of the listed devices for the given userId,
+ // and it's not already restricting the mix on a userId,
+ // modify its rules to exclude the userId
+ if (!mix->hasUserIdRule(false /*match*/, userId)) {
+ // no need to do it again if userId is already excluded
+ mix->setExcludeUserId(userId);
+ }
+ }
+ }
+
+ return NO_ERROR;
+}
+
+status_t AudioPolicyMixCollection::removeUserIdDeviceAffinities(int userId) {
+ // for each player mix: remove existing rules that match or exclude this userId
+ for (size_t i = 0; i < size(); i++) {
+ bool foundUserIdRule = false;
+ const AudioPolicyMix *mix = itemAt(i).get();
+ if (!mix->isDeviceAffinityCompatible()) {
+ continue;
+ }
+ std::vector<size_t> criteriaToRemove;
+ for (size_t j = 0; j < mix->mCriteria.size(); j++) {
+ const uint32_t rule = mix->mCriteria[j].mRule;
+ // is this rule excluding the userId? (not considering userId match rules
+ // as those are not used for userId-device affinity)
+ if (rule == RULE_EXCLUDE_USERID
+ && userId == mix->mCriteria[j].mValue.mUserId) {
+ foundUserIdRule = true;
+ criteriaToRemove.insert(criteriaToRemove.begin(), j);
+ }
+ }
+ if (foundUserIdRule) {
+ for (size_t j = 0; j < criteriaToRemove.size(); j++) {
+ mix->mCriteria.removeAt(criteriaToRemove[j]);
+ }
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t AudioPolicyMixCollection::getDevicesForUserId(int userId,
+ Vector<AudioDeviceTypeAddr>& devices) const {
+ // for each player mix:
+ // find rules that don't exclude this userId, and add the device to the list
+ for (size_t i = 0; i < size(); i++) {
+ bool ruleAllowsUserId = true;
+ const AudioPolicyMix *mix = itemAt(i).get();
+ if (mix->mMixType != MIX_TYPE_PLAYERS) {
+ continue;
+ }
+ for (size_t j = 0; j < mix->mCriteria.size(); j++) {
+ const uint32_t rule = mix->mCriteria[j].mRule;
+ if (rule == RULE_EXCLUDE_USERID
+ && userId == mix->mCriteria[j].mValue.mUserId) {
+ ruleAllowsUserId = false;
+ break;
+ }
+ }
+ if (ruleAllowsUserId) {
+ devices.add(AudioDeviceTypeAddr(mix->mDeviceType, mix->mDeviceAddress.string()));
+ }
+ }
+ return NO_ERROR;
+}
+
void AudioPolicyMixCollection::dump(String8 *dst) const
{
dst->append("\nAudio Policy Mix:\n");
diff --git a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
index 1dc7020..95822b9 100644
--- a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
@@ -82,14 +82,13 @@
}
SourceClientDescriptor::SourceClientDescriptor(audio_port_handle_t portId, uid_t uid,
- audio_attributes_t attributes, const sp<AudioPatch>& patchDesc,
+ audio_attributes_t attributes, const struct audio_port_config &config,
const sp<DeviceDescriptor>& srcDevice, audio_stream_type_t stream,
product_strategy_t strategy, VolumeSource volumeSource) :
TrackClientDescriptor::TrackClientDescriptor(portId, uid, AUDIO_SESSION_NONE, attributes,
- AUDIO_CONFIG_BASE_INITIALIZER, AUDIO_PORT_HANDLE_NONE,
+ {config.sample_rate, config.channel_mask, config.format}, AUDIO_PORT_HANDLE_NONE,
stream, strategy, volumeSource, AUDIO_OUTPUT_FLAG_NONE, false,
- {} /* Sources do not support secondary outputs*/),
- mPatchDesc(patchDesc), mSrcDevice(srcDevice)
+ {} /* Sources do not support secondary outputs*/), mSrcDevice(srcDevice)
{
}
diff --git a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
index 4376802..1b2f7c7 100644
--- a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
@@ -200,6 +200,7 @@
{
static constexpr const char *speakerDrcEnabled = "speaker_drc_enabled";
static constexpr const char *callScreenModeSupported= "call_screen_mode_supported";
+ static constexpr const char *engineLibrarySuffix = "engine_library";
};
static status_t deserialize(const xmlNode *root, AudioPolicyConfig *config);
@@ -692,6 +693,11 @@
convertTo<std::string, bool>(attr, value)) {
config->setCallScreenModeSupported(value);
}
+ std::string engineLibrarySuffix = getXmlAttribute(cur, Attributes::engineLibrarySuffix);
+ if (!engineLibrarySuffix.empty()) {
+ config->setEngineLibraryNameSuffix(engineLibrarySuffix);
+ }
+ return NO_ERROR;
}
}
return NO_ERROR;
diff --git a/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp b/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp
index 2b5455e..c5b3546 100644
--- a/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp
@@ -55,9 +55,11 @@
MAKE_STRING_FROM_ENUM(RULE_MATCH_ATTRIBUTE_USAGE),
MAKE_STRING_FROM_ENUM(RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET),
MAKE_STRING_FROM_ENUM(RULE_MATCH_UID),
+ MAKE_STRING_FROM_ENUM(RULE_MATCH_USERID),
MAKE_STRING_FROM_ENUM(RULE_EXCLUDE_ATTRIBUTE_USAGE),
MAKE_STRING_FROM_ENUM(RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET),
MAKE_STRING_FROM_ENUM(RULE_EXCLUDE_UID),
+ MAKE_STRING_FROM_ENUM(RULE_EXCLUDE_USERID),
TERMINATOR
};
diff --git a/services/audiopolicy/config/Android.bp b/services/audiopolicy/config/Android.bp
index 4b5e788..f4610bb 100644
--- a/services/audiopolicy/config/Android.bp
+++ b/services/audiopolicy/config/Android.bp
@@ -92,6 +92,10 @@
srcs: ["audio_policy_configuration_generic.xml"],
}
filegroup {
+ name: "audio_policy_configuration_generic_configurable",
+ srcs: ["audio_policy_configuration_generic_configurable.xml"],
+}
+filegroup {
name: "usb_audio_policy_configuration",
srcs: ["usb_audio_policy_configuration.xml"],
}
diff --git a/services/audiopolicy/config/audio_policy_configuration_generic_configurable.xml b/services/audiopolicy/config/audio_policy_configuration_generic_configurable.xml
new file mode 100644
index 0000000..fbe4f7f
--- /dev/null
+++ b/services/audiopolicy/config/audio_policy_configuration_generic_configurable.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- 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.
+-->
+
+<audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <!-- version section contains a “version” tag in the form “major.minor” e.g version=”1.0” -->
+
+ <!-- Global configuration Decalaration -->
+ <globalConfiguration speaker_drc_enabled="false" engine_library="configurable"/>
+
+ <modules>
+ <!-- Primary Audio HAL -->
+ <xi:include href="primary_audio_policy_configuration.xml"/>
+
+ <!-- Remote Submix Audio HAL -->
+ <xi:include href="r_submix_audio_policy_configuration.xml"/>
+
+ </modules>
+ <!-- End of Modules section -->
+
+ <!-- Volume section:
+ IMPORTANT NOTE: Volume tables have been moved to engine configuration.
+ Keep it here for legacy.
+ Engine will fallback on these files if none are provided by engine.
+ -->
+
+ <xi:include href="audio_policy_volumes.xml"/>
+ <xi:include href="default_volume_tables.xml"/>
+
+ <!-- End of Volume section -->
+
+ <!-- Surround Sound configuration -->
+
+ <xi:include href="surround_sound_configuration_5_0.xml"/>
+
+ <!-- End of Surround Sound configuration -->
+
+</audioPolicyConfiguration>
diff --git a/services/audiopolicy/config/audio_policy_volumes.xml b/services/audiopolicy/config/audio_policy_volumes.xml
index 27bd3ff..1dec6f4 100644
--- a/services/audiopolicy/config/audio_policy_volumes.xml
+++ b/services/audiopolicy/config/audio_policy_volumes.xml
@@ -181,6 +181,16 @@
ref="DEFAULT_NON_MUTABLE_VOLUME_CURVE"/>
<volume stream="AUDIO_STREAM_ACCESSIBILITY" deviceCategory="DEVICE_CATEGORY_HEARING_AID"
ref="DEFAULT_NON_MUTABLE_HEARING_AID_VOLUME_CURVE"/>
+ <volume stream="AUDIO_STREAM_ASSISTANT" deviceCategory="DEVICE_CATEGORY_HEADSET"
+ ref="DEFAULT_MEDIA_VOLUME_CURVE"/>
+ <volume stream="AUDIO_STREAM_ASSISTANT" deviceCategory="DEVICE_CATEGORY_SPEAKER"
+ ref="DEFAULT_DEVICE_CATEGORY_SPEAKER_VOLUME_CURVE"/>
+ <volume stream="AUDIO_STREAM_ASSISTANT" deviceCategory="DEVICE_CATEGORY_EARPIECE"
+ ref="DEFAULT_MEDIA_VOLUME_CURVE"/>
+ <volume stream="AUDIO_STREAM_ASSISTANT" deviceCategory="DEVICE_CATEGORY_EXT_MEDIA"
+ ref="DEFAULT_MEDIA_VOLUME_CURVE"/>
+ <volume stream="AUDIO_STREAM_ASSISTANT" deviceCategory="DEVICE_CATEGORY_HEARING_AID"
+ ref="DEFAULT_HEARING_AID_VOLUME_CURVE"/>
<volume stream="AUDIO_STREAM_REROUTING" deviceCategory="DEVICE_CATEGORY_HEADSET"
ref="FULL_SCALE_VOLUME_CURVE"/>
<volume stream="AUDIO_STREAM_REROUTING" deviceCategory="DEVICE_CATEGORY_SPEAKER"
diff --git a/services/audiopolicy/engine/common/src/EngineBase.cpp b/services/audiopolicy/engine/common/src/EngineBase.cpp
index 01efc7a..525e965 100644
--- a/services/audiopolicy/engine/common/src/EngineBase.cpp
+++ b/services/audiopolicy/engine/common/src/EngineBase.cpp
@@ -106,48 +106,33 @@
engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig()
{
- auto loadProductStrategies =
- [](auto& strategyConfigs, auto& productStrategies, auto& volumeGroups) {
- for (auto& strategyConfig : strategyConfigs) {
- sp<ProductStrategy> strategy = new ProductStrategy(strategyConfig.name);
- for (const auto &group : strategyConfig.attributesGroups) {
- const auto &iter = std::find_if(begin(volumeGroups), end(volumeGroups),
- [&group](const auto &volumeGroup) {
- return group.volumeGroup == volumeGroup.second->getName(); });
- ALOG_ASSERT(iter != end(volumeGroups), "Invalid Volume Group Name %s",
- group.volumeGroup.c_str());
- if (group.stream != AUDIO_STREAM_DEFAULT) {
- iter->second->addSupportedStream(group.stream);
- }
- for (const auto &attr : group.attributesVect) {
- strategy->addAttributes({group.stream, iter->second->getId(), attr});
- iter->second->addSupportedAttributes(attr);
- }
- }
- product_strategy_t strategyId = strategy->getId();
- productStrategies[strategyId] = strategy;
- }
- };
- auto loadVolumeGroups = [](auto &volumeConfigs, auto &volumeGroups) {
- for (auto &volumeConfig : volumeConfigs) {
- sp<VolumeGroup> volumeGroup = new VolumeGroup(volumeConfig.name, volumeConfig.indexMin,
- volumeConfig.indexMax);
- volumeGroups[volumeGroup->getId()] = volumeGroup;
+ auto loadVolumeConfig = [](auto &volumeGroups, auto &volumeConfig) {
+ sp<VolumeGroup> volumeGroup = new VolumeGroup(volumeConfig.name, volumeConfig.indexMin,
+ volumeConfig.indexMax);
+ volumeGroups[volumeGroup->getId()] = volumeGroup;
- for (auto &configCurve : volumeConfig.volumeCurves) {
- device_category deviceCat = DEVICE_CATEGORY_SPEAKER;
- if (!DeviceCategoryConverter::fromString(configCurve.deviceCategory, deviceCat)) {
- ALOGE("%s: Invalid %s", __FUNCTION__, configCurve.deviceCategory.c_str());
- continue;
- }
- sp<VolumeCurve> curve = new VolumeCurve(deviceCat);
- for (auto &point : configCurve.curvePoints) {
- curve->add({point.index, point.attenuationInMb});
- }
- volumeGroup->add(curve);
+ for (auto &configCurve : volumeConfig.volumeCurves) {
+ device_category deviceCat = DEVICE_CATEGORY_SPEAKER;
+ if (!DeviceCategoryConverter::fromString(configCurve.deviceCategory, deviceCat)) {
+ ALOGE("%s: Invalid %s", __FUNCTION__, configCurve.deviceCategory.c_str());
+ continue;
}
+ sp<VolumeCurve> curve = new VolumeCurve(deviceCat);
+ for (auto &point : configCurve.curvePoints) {
+ curve->add({point.index, point.attenuationInMb});
+ }
+ volumeGroup->add(curve);
+ }
+ return volumeGroup;
+ };
+ auto addSupportedStreamAttributes = [](auto &group, auto &volumeGroup, auto &strategy) {
+ volumeGroup->addSupportedStream(group.stream);
+ for (const auto &attr : group.attributesVect) {
+ strategy->addAttributes({group.stream, volumeGroup->getId(), attr});
+ volumeGroup->addSupportedAttributes(attr);
}
};
+
auto result = engineConfig::parse();
if (result.parsedConfig == nullptr) {
ALOGW("%s: No configuration found, using default matching phone experience.", __FUNCTION__);
@@ -156,10 +141,47 @@
result = {std::make_unique<engineConfig::Config>(config),
static_cast<size_t>(ret == NO_ERROR ? 0 : 1)};
}
+ // Append for internal use only strategies/volume groups (e.g. rerouting/patch)
+ result.parsedConfig->productStrategies.insert(
+ std::end(result.parsedConfig->productStrategies),
+ std::begin(gOrderedSystemStrategies), std::end(gOrderedSystemStrategies));
+
+ result.parsedConfig->volumeGroups.insert(
+ std::end(result.parsedConfig->volumeGroups),
+ std::begin(gSystemVolumeGroups), std::end(gSystemVolumeGroups));
+
ALOGE_IF(result.nbSkippedElement != 0, "skipped %zu elements", result.nbSkippedElement);
- loadVolumeGroups(result.parsedConfig->volumeGroups, mVolumeGroups);
- loadProductStrategies(result.parsedConfig->productStrategies, mProductStrategies,
- mVolumeGroups);
+
+ engineConfig::VolumeGroup defaultVolumeConfig;
+ for (auto &volumeConfig : result.parsedConfig->volumeGroups) {
+ // save default volume config for streams not defined in configuration
+ if (volumeConfig.name.compare("AUDIO_STREAM_MUSIC") == 0) {
+ defaultVolumeConfig = volumeConfig;
+ }
+ loadVolumeConfig(mVolumeGroups, volumeConfig);
+ }
+ for (auto& strategyConfig : result.parsedConfig->productStrategies) {
+ sp<ProductStrategy> strategy = new ProductStrategy(strategyConfig.name);
+ for (const auto &group : strategyConfig.attributesGroups) {
+ const auto &iter = std::find_if(begin(mVolumeGroups), end(mVolumeGroups),
+ [&group](const auto &volumeGroup) {
+ return group.volumeGroup == volumeGroup.second->getName(); });
+ if (group.stream != AUDIO_STREAM_DEFAULT) {
+ if (iter == end(mVolumeGroups)) {
+ ALOGW("%s: No configuration of %s found, using default volume configuration"
+ , __FUNCTION__, group.volumeGroup.c_str());
+ defaultVolumeConfig.name = group.volumeGroup;
+ sp<VolumeGroup> volumeGroup =
+ loadVolumeConfig(mVolumeGroups, defaultVolumeConfig);
+ addSupportedStreamAttributes(group, volumeGroup, strategy);
+ } else {
+ addSupportedStreamAttributes(group, iter->second, strategy);
+ }
+ }
+ }
+ product_strategy_t strategyId = strategy->getId();
+ mProductStrategies[strategyId] = strategy;
+ }
mProductStrategies.initialize();
return result;
}
diff --git a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h
index 20c57ee..3366814 100644
--- a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h
+++ b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h
@@ -81,6 +81,10 @@
},
{"STRATEGY_MEDIA",
{
+ {"assistant", AUDIO_STREAM_ASSISTANT, "AUDIO_STREAM_ASSISTANT",
+ {{AUDIO_CONTENT_TYPE_SPEECH, AUDIO_USAGE_ASSISTANT,
+ AUDIO_SOURCE_DEFAULT, 0, ""}}
+ },
{"music", AUDIO_STREAM_MUSIC, "AUDIO_STREAM_MUSIC",
{
{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_MEDIA, AUDIO_SOURCE_DEFAULT, 0, ""},
@@ -121,15 +125,22 @@
AUDIO_FLAG_BEACON, ""}}
}
},
- },
- {"STRATEGY_REROUTING",
+ }
+};
+
+/**
+ * For Internal use of respectively audio policy and audioflinger
+ * For compatibility reason why apm volume config file, volume group name is the stream type.
+ */
+const engineConfig::ProductStrategies gOrderedSystemStrategies = {
+ {"rerouting",
{
{"", AUDIO_STREAM_REROUTING, "AUDIO_STREAM_REROUTING",
{{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT, 0, ""}}
}
},
},
- {"STRATEGY_PATCH",
+ {"patch",
{
{"", AUDIO_STREAM_PATCH, "AUDIO_STREAM_PATCH",
{{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT, 0, ""}}
@@ -137,6 +148,28 @@
},
}
};
+const engineConfig::VolumeGroups gSystemVolumeGroups = {
+ {"AUDIO_STREAM_REROUTING", 0, 1,
+ {
+ {"DEVICE_CATEGORY_SPEAKER", {{0,0}, {100, 0}}},
+ {"DEVICE_CATEGORY_HEADSET", {{0,0}, {100, 0}}},
+ {"DEVICE_CATEGORY_EARPIECE", {{0,0}, {100, 0}}},
+ {"DEVICE_CATEGORY_EXT_MEDIA", {{0,0}, {100, 0}}},
+ {"DEVICE_CATEGORY_HEARING_AID", {{0,0}, {100, 0}}},
+
+ }
+ },
+ {"AUDIO_STREAM_PATCH", 0, 1,
+ {
+ {"DEVICE_CATEGORY_SPEAKER", {{0,0}, {100, 0}}},
+ {"DEVICE_CATEGORY_HEADSET", {{0,0}, {100, 0}}},
+ {"DEVICE_CATEGORY_EARPIECE", {{0,0}, {100, 0}}},
+ {"DEVICE_CATEGORY_EXT_MEDIA", {{0,0}, {100, 0}}},
+ {"DEVICE_CATEGORY_HEARING_AID", {{0,0}, {100, 0}}},
+
+ }
+ }
+};
const engineConfig::Config gDefaultEngineConfig = {
1.0,
diff --git a/services/audiopolicy/engine/config/Android.bp b/services/audiopolicy/engine/config/Android.bp
index 885b5fa..ff840f9 100644
--- a/services/audiopolicy/engine/config/Android.bp
+++ b/services/audiopolicy/engine/config/Android.bp
@@ -1,4 +1,4 @@
-cc_library_static {
+cc_library {
name: "libaudiopolicyengine_config",
export_include_dirs: ["include"],
include_dirs: [
@@ -14,17 +14,14 @@
],
shared_libs: [
"libmedia_helper",
- "libandroidicu",
"libxml2",
"libutils",
"liblog",
"libcutils",
],
- static_libs: [
- "libaudiopolicycomponents",
- ],
header_libs: [
"libaudio_system_headers",
- "libaudiopolicycommon",
+ "libmedia_headers",
+ "libaudioclient_headers",
],
}
diff --git a/services/audiopolicy/engine/config/src/EngineConfig.cpp b/services/audiopolicy/engine/config/src/EngineConfig.cpp
index d47fbd2..7f8cdd9 100644
--- a/services/audiopolicy/engine/config/src/EngineConfig.cpp
+++ b/services/audiopolicy/engine/config/src/EngineConfig.cpp
@@ -18,7 +18,6 @@
//#define LOG_NDEBUG 0
#include "EngineConfig.h"
-#include <policy.h>
#include <cutils/properties.h>
#include <media/TypeConverter.h>
#include <media/convert.h>
diff --git a/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_product_strategies.xml b/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_product_strategies.xml
index 0ee83a2..f598cf2 100644
--- a/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_product_strategies.xml
+++ b/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_product_strategies.xml
@@ -84,7 +84,7 @@
</AttributesGroup>
</ProductStrategy>
<ProductStrategy name="voice_command">
- <AttributesGroup volumeGroup="speech">
+ <AttributesGroup volumeGroup="speech" streamType="AUDIO_STREAM_ASSISTANT">
<Attributes>
<ContentType value="AUDIO_CONTENT_TYPE_SPEECH"/>
<Usage value="AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE"/>
@@ -147,10 +147,6 @@
<ProductStrategy name="notification">
<AttributesGroup streamType="AUDIO_STREAM_NOTIFICATION" volumeGroup="ring">
<Attributes> <Usage value="AUDIO_USAGE_NOTIFICATION"/> </Attributes>
- <Attributes> <Usage value="AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT"/> </Attributes>
- <Attributes> <Usage value="AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED"/> </Attributes>
- <Attributes> <Usage value="AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST"/> </Attributes>
- <Attributes> <Usage value="AUDIO_USAGE_NOTIFICATION_EVENT"/> </Attributes>
</AttributesGroup>
</ProductStrategy>
<ProductStrategy name="system">
@@ -167,19 +163,5 @@
</AttributesGroup>
</ProductStrategy>
- <!-- Routing Strategy rerouting may be removed as following media??? -->
- <ProductStrategy name="rerouting">
- <AttributesGroup streamType="AUDIO_STREAM_REROUTING" volumeGroup="rerouting">
- <Attributes></Attributes>
- </AttributesGroup>
- </ProductStrategy>
-
- <!-- Patch stream needs full scale volume, define it otherwise switch to default... -->
- <ProductStrategy name="patch">
- <AttributesGroup streamType="AUDIO_STREAM_PATCH" volumeGroup="patch">
- <Attributes></Attributes>
- </AttributesGroup>
- </ProductStrategy>
-
</ProductStrategies>
diff --git a/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_volumes.xml b/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_volumes.xml
index 6e72dc5..97a25a8 100644
--- a/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_volumes.xml
+++ b/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_volumes.xml
@@ -189,25 +189,5 @@
</volume>
</volumeGroup>
- <volumeGroup>
- <name>rerouting</name>
- <indexMin>0</indexMin>
- <indexMax>1</indexMax>
- <volume deviceCategory="DEVICE_CATEGORY_HEADSET">
- <point>0,0</point>
- <point>100,0</point>
- </volume>
- </volumeGroup>
-
- <volumeGroup>
- <name>patch</name>
- <indexMin>0</indexMin>
- <indexMax>1</indexMax>
- <volume deviceCategory="DEVICE_CATEGORY_HEADSET">
- <point>0,0</point>
- <point>100,0</point>
- </volume>
- </volumeGroup>
-
</volumeGroups>
diff --git a/services/audiopolicy/engineconfigurable/config/example/caremu/audio_policy_engine_product_strategies.xml b/services/audiopolicy/engineconfigurable/config/example/caremu/audio_policy_engine_product_strategies.xml
index adcbd83..f598cf2 100644
--- a/services/audiopolicy/engineconfigurable/config/example/caremu/audio_policy_engine_product_strategies.xml
+++ b/services/audiopolicy/engineconfigurable/config/example/caremu/audio_policy_engine_product_strategies.xml
@@ -84,7 +84,7 @@
</AttributesGroup>
</ProductStrategy>
<ProductStrategy name="voice_command">
- <AttributesGroup volumeGroup="speech">
+ <AttributesGroup volumeGroup="speech" streamType="AUDIO_STREAM_ASSISTANT">
<Attributes>
<ContentType value="AUDIO_CONTENT_TYPE_SPEECH"/>
<Usage value="AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE"/>
@@ -147,10 +147,6 @@
<ProductStrategy name="notification">
<AttributesGroup streamType="AUDIO_STREAM_NOTIFICATION" volumeGroup="ring">
<Attributes> <Usage value="AUDIO_USAGE_NOTIFICATION"/> </Attributes>
- <Attributes> <Usage value="AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT"/> </Attributes>
- <Attributes> <Usage value="AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED"/> </Attributes>
- <Attributes> <Usage value="AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST"/> </Attributes>
- <Attributes> <Usage value="AUDIO_USAGE_NOTIFICATION_EVENT"/> </Attributes>
</AttributesGroup>
</ProductStrategy>
<ProductStrategy name="system">
@@ -167,18 +163,5 @@
</AttributesGroup>
</ProductStrategy>
- <!-- Routing Strategy rerouting may be removed as following media??? -->
- <ProductStrategy name="rerouting">
- <AttributesGroup streamType="AUDIO_STREAM_REROUTING" volumeGroup="rerouting">
- <Attributes></Attributes>
- </AttributesGroup>
- </ProductStrategy>
-
- <!-- Patch stream needs full scale volume, define it otherwise switch to default... -->
- <ProductStrategy name="patch">
- <AttributesGroup streamType="AUDIO_STREAM_PATCH" volumeGroup="patch">
- <Attributes></Attributes>
- </AttributesGroup>
- </ProductStrategy>
</ProductStrategies>
diff --git a/services/audiopolicy/engineconfigurable/config/example/caremu/audio_policy_engine_volumes.xml b/services/audiopolicy/engineconfigurable/config/example/caremu/audio_policy_engine_volumes.xml
index 6e72dc5..97a25a8 100644
--- a/services/audiopolicy/engineconfigurable/config/example/caremu/audio_policy_engine_volumes.xml
+++ b/services/audiopolicy/engineconfigurable/config/example/caremu/audio_policy_engine_volumes.xml
@@ -189,25 +189,5 @@
</volume>
</volumeGroup>
- <volumeGroup>
- <name>rerouting</name>
- <indexMin>0</indexMin>
- <indexMax>1</indexMax>
- <volume deviceCategory="DEVICE_CATEGORY_HEADSET">
- <point>0,0</point>
- <point>100,0</point>
- </volume>
- </volumeGroup>
-
- <volumeGroup>
- <name>patch</name>
- <indexMin>0</indexMin>
- <indexMax>1</indexMax>
- <volume deviceCategory="DEVICE_CATEGORY_HEADSET">
- <point>0,0</point>
- <point>100,0</point>
- </volume>
- </volumeGroup>
-
</volumeGroups>
diff --git a/services/audiopolicy/engineconfigurable/config/example/common/audio_policy_engine_criterion_types.xml.in b/services/audiopolicy/engineconfigurable/config/example/common/audio_policy_engine_criterion_types.xml.in
index fe17369..e134c42 100644
--- a/services/audiopolicy/engineconfigurable/config/example/common/audio_policy_engine_criterion_types.xml.in
+++ b/services/audiopolicy/engineconfigurable/config/example/common/audio_policy_engine_criterion_types.xml.in
@@ -22,7 +22,12 @@
<value literal="0" numerical="1"/>
</values>
</criterion_type>
- <criterion_type name="InputDevicesAddressesType" type="inclusive"/>
+ <criterion_type name="InputDevicesAddressesType" type="inclusive">
+ <values>
+ <!-- legacy remote submix -->
+ <value literal="0" numerical="1"/>
+ </values>
+ </criterion_type>
<criterion_type name="AndroidModeType" type="exclusive"/>
<criterion_type name="BooleanType" type="exclusive">
<values>
diff --git a/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_product_strategies.xml b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_product_strategies.xml
index 9398743..a7388da 100644
--- a/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_product_strategies.xml
+++ b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_product_strategies.xml
@@ -72,6 +72,12 @@
<Attributes> <Usage value="AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE"/> </Attributes>
<Attributes></Attributes>
</AttributesGroup>
+ <AttributesGroup streamType="AUDIO_STREAM_ASSISTANT" volumeGroup="assistant">
+ <Attributes>
+ <ContentType value="AUDIO_CONTENT_TYPE_SPEECH"/>
+ <Usage value="AUDIO_USAGE_ASSISTANT"/>
+ </Attributes>
+ </AttributesGroup>
<AttributesGroup streamType="AUDIO_STREAM_SYSTEM" volumeGroup="system">
<Attributes> <Usage value="AUDIO_USAGE_ASSISTANCE_SONIFICATION"/> </Attributes>
</AttributesGroup>
@@ -91,20 +97,5 @@
</AttributesGroup>
</ProductStrategy>
- <!-- Routing Strategy rerouting may be removed as following media??? -->
- <ProductStrategy name="STRATEGY_REROUTING">
- <AttributesGroup streamType="AUDIO_STREAM_REROUTING" volumeGroup="rerouting">
- <Attributes></Attributes>
- </AttributesGroup>
- </ProductStrategy>
-
- <!-- Default product strategy has empty attributes -->
- <ProductStrategy name="STRATEGY_PATCH">
- <AttributesGroup streamType="AUDIO_STREAM_PATCH" volumeGroup="patch">
- <Attributes></Attributes>
- </AttributesGroup>
- </ProductStrategy>
-
-
</ProductStrategies>
diff --git a/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_stream_volumes.xml b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_stream_volumes.xml
index 707a184..8aa71ca 100644
--- a/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_stream_volumes.xml
+++ b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_stream_volumes.xml
@@ -205,27 +205,16 @@
<volume deviceCategory="DEVICE_CATEGORY_EXT_MEDIA" ref="DEFAULT_NON_MUTABLE_VOLUME_CURVE"/>
<volume deviceCategory="DEVICE_CATEGORY_HEARING_AID" ref="DEFAULT_NON_MUTABLE_HEARING_AID_VOLUME_CURVE"/>
</volumeGroup>
-
<volumeGroup>
- <name>rerouting</name>
+ <name>assistant</name>
<indexMin>0</indexMin>
- <indexMax>1</indexMax>
- <volume deviceCategory="DEVICE_CATEGORY_HEADSET" ref="FULL_SCALE_VOLUME_CURVE"/>
- <volume deviceCategory="DEVICE_CATEGORY_SPEAKER" ref="FULL_SCALE_VOLUME_CURVE"/>
- <volume deviceCategory="DEVICE_CATEGORY_EARPIECE" ref="FULL_SCALE_VOLUME_CURVE"/>
- <volume deviceCategory="DEVICE_CATEGORY_EXT_MEDIA" ref="FULL_SCALE_VOLUME_CURVE"/>
- <volume deviceCategory="DEVICE_CATEGORY_HEARING_AID" ref="FULL_SCALE_VOLUME_CURVE"/>
+ <indexMax>15</indexMax>
+ <volume deviceCategory="DEVICE_CATEGORY_HEADSET" ref="DEFAULT_MEDIA_VOLUME_CURVE"/>
+ <volume deviceCategory="DEVICE_CATEGORY_SPEAKER" ref="DEFAULT_DEVICE_CATEGORY_SPEAKER_VOLUME_CURVE"/>
+ <volume deviceCategory="DEVICE_CATEGORY_EARPIECE" ref="DEFAULT_MEDIA_VOLUME_CURVE"/>
+ <volume deviceCategory="DEVICE_CATEGORY_EXT_MEDIA" ref="DEFAULT_MEDIA_VOLUME_CURVE"/>
+ <volume deviceCategory="DEVICE_CATEGORY_HEARING_AID" ref="DEFAULT_HEARING_AID_VOLUME_CURVE"/>
</volumeGroup>
- <volumeGroup>
- <name>patch</name>
- <indexMin>0</indexMin>
- <indexMax>1</indexMax>
- <volume deviceCategory="DEVICE_CATEGORY_HEADSET" ref="FULL_SCALE_VOLUME_CURVE"/>
- <volume deviceCategory="DEVICE_CATEGORY_SPEAKER" ref="FULL_SCALE_VOLUME_CURVE"/>
- <volume deviceCategory="DEVICE_CATEGORY_EARPIECE" ref="FULL_SCALE_VOLUME_CURVE"/>
- <volume deviceCategory="DEVICE_CATEGORY_EXT_MEDIA" ref="FULL_SCALE_VOLUME_CURVE"/>
- <volume deviceCategory="DEVICE_CATEGORY_HEARING_AID" ref="FULL_SCALE_VOLUME_CURVE"/>
- </volumeGroup>
</volumeGroups>
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/Android.bp b/services/audiopolicy/engineconfigurable/parameter-framework/Android.bp
index a0b874a..90ebffd 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/Android.bp
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/Android.bp
@@ -31,9 +31,13 @@
prebuilt_etc {
name: "PolicySubsystem-CommonTypes.xml",
vendor: true,
- src: ":PolicySubsystem-CommonTypes",
+ src: ":buildcommontypesstructure_gen",
sub_dir: "parameter-framework/Structure/Policy",
}
+genrule {
+ name: "buildcommontypesstructure_gen",
+ defaults: ["buildcommontypesstructurerule"],
+}
filegroup {
name: "product_strategies_structure_template",
@@ -48,8 +52,8 @@
srcs: ["examples/common/Structure/PolicySubsystem-no-strategy.xml"],
}
filegroup {
- name: "PolicySubsystem-CommonTypes",
- srcs: ["examples/common/Structure/PolicySubsystem-CommonTypes.xml"],
+ name: "common_types_structure_template",
+ srcs: ["examples/common/Structure/PolicySubsystem-CommonTypes.xml.in"],
}
filegroup {
name: "PolicyClass",
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.bp b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.bp
index 5078268..82b1b6d 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.bp
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.bp
@@ -85,7 +85,7 @@
srcs: [
":PolicyClass",
":PolicySubsystem",
- ":PolicySubsystem-CommonTypes",
+ ":buildcommontypesstructure_gen",
":buildstrategiesstructure_gen",
],
}
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Settings/device_for_product_strategies.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Settings/device_for_product_strategies.pfw
index 57ad592..ddae356 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Settings/device_for_product_strategies.pfw
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Settings/device_for_product_strategies.pfw
@@ -14,7 +14,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -59,7 +59,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -106,7 +106,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -152,7 +152,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -205,7 +205,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -251,7 +251,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -304,7 +304,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -357,7 +357,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -411,7 +411,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -464,7 +464,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -517,7 +517,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -570,7 +570,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -623,7 +623,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -676,7 +676,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -729,7 +729,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/CarEmu/Android.bp b/services/audiopolicy/engineconfigurable/parameter-framework/examples/CarEmu/Android.bp
index 0917440..e4605b2 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/CarEmu/Android.bp
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/CarEmu/Android.bp
@@ -86,7 +86,7 @@
srcs: [
":PolicyClass",
":PolicySubsystem",
- ":PolicySubsystem-CommonTypes",
+ ":buildcommontypesstructure_gen",
":buildstrategiesstructure_gen",
],
}
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/CarEmu/Settings/device_for_product_strategies.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/CarEmu/Settings/device_for_product_strategies.pfw
index ca3464f..cc778df 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/CarEmu/Settings/device_for_product_strategies.pfw
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/CarEmu/Settings/device_for_product_strategies.pfw
@@ -14,7 +14,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -59,7 +59,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -106,7 +106,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -153,7 +153,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -198,7 +198,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -245,7 +245,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -291,7 +291,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -337,7 +337,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -384,7 +384,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -430,7 +430,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -476,7 +476,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -522,7 +522,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -568,7 +568,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -614,7 +614,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -659,7 +659,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.bp b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.bp
index 11e220b..61b54cf 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.bp
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.bp
@@ -94,7 +94,7 @@
srcs: [
":PolicyClass",
":PolicySubsystem",
- ":PolicySubsystem-CommonTypes",
+ ":buildcommontypesstructure_gen",
":buildstrategiesstructure_gen",
],
}
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_accessibility.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_accessibility.pfw
index 53e93de..d16a904 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_accessibility.pfw
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_accessibility.pfw
@@ -45,7 +45,7 @@
wired_headset = 0
wired_headphone = 0
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -73,7 +73,7 @@
wired_headset = 0
wired_headphone = 0
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -101,7 +101,7 @@
wired_headset = 0
wired_headphone = 0
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -129,7 +129,7 @@
wired_headset = 0
wired_headphone = 0
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -157,7 +157,7 @@
wired_headset = 0
wired_headphone = 0
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -186,7 +186,7 @@
wired_headset = 0
wired_headphone = 0
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -215,7 +215,7 @@
wired_headset = 0
wired_headphone = 0
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -244,7 +244,7 @@
wired_headset = 0
wired_headphone = 0
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -281,7 +281,7 @@
wired_headset = 0
wired_headphone = 1
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -317,7 +317,7 @@
wired_headset = 0
wired_headphone = 0
line = 1
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -354,7 +354,7 @@
wired_headset = 1
wired_headphone = 0
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -394,7 +394,7 @@
wired_headset = 0
wired_headphone = 0
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 1
@@ -425,7 +425,7 @@
wired_headset = 0
wired_headphone = 0
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 1
usb_device = 0
@@ -455,7 +455,7 @@
wired_headset = 0
wired_headphone = 0
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 1
usb_accessory = 0
usb_device = 0
@@ -485,7 +485,7 @@
wired_headset = 0
wired_headphone = 0
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -517,7 +517,7 @@
wired_headset = 0
wired_headphone = 0
line = 0
- angl_dock_headset = 1
+ anlg_dock_headset = 1
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -546,7 +546,7 @@
wired_headset = 0
wired_headphone = 0
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -568,7 +568,7 @@
wired_headset = 0
wired_headphone = 0
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -588,7 +588,7 @@
wired_headset = 0
wired_headphone = 0
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_dtmf.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_dtmf.pfw
index b8426c6..414445d 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_dtmf.pfw
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_dtmf.pfw
@@ -34,7 +34,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -62,7 +62,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -90,7 +90,7 @@
bluetooth_a2dp_headphones = 1
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -118,7 +118,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 1
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -147,7 +147,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -176,7 +176,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -205,7 +205,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -242,7 +242,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -281,7 +281,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -318,7 +318,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -358,7 +358,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 1
@@ -389,7 +389,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 1
usb_device = 0
@@ -419,7 +419,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 1
usb_accessory = 0
usb_device = 0
@@ -449,7 +449,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 1
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -481,7 +481,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 1
+ anlg_dock_headset = 1
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -510,7 +510,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -548,7 +548,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -568,7 +568,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_enforced_audible.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_enforced_audible.pfw
index 2daa9ac..36b8f3c 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_enforced_audible.pfw
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_enforced_audible.pfw
@@ -77,7 +77,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -100,7 +100,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -123,7 +123,7 @@
bluetooth_a2dp_headphones = 1
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -146,7 +146,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 1
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -169,7 +169,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -192,7 +192,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -215,7 +215,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -238,7 +238,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 1
usb_device = 0
@@ -261,7 +261,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 1
@@ -284,7 +284,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 1
usb_accessory = 0
usb_device = 0
@@ -307,7 +307,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 1
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -331,7 +331,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 1
+ anlg_dock_headset = 1
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -351,7 +351,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_media.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_media.pfw
index d6d355c..6210a57 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_media.pfw
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_media.pfw
@@ -26,7 +26,7 @@
speaker = 0
hdmi = 0
dgtl_dock_headset = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
usb_device = 0
usb_accessory = 0
wired_headset = 0
@@ -46,7 +46,7 @@
speaker = 0
hdmi = 0
dgtl_dock_headset = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
usb_device = 0
usb_accessory = 0
wired_headset = 0
@@ -66,7 +66,7 @@
speaker = 0
hdmi = 0
dgtl_dock_headset = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
usb_device = 0
usb_accessory = 0
wired_headset = 0
@@ -86,7 +86,7 @@
speaker = 0
hdmi = 0
dgtl_dock_headset = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
usb_device = 0
usb_accessory = 0
wired_headset = 0
@@ -109,7 +109,7 @@
speaker = 1
hdmi = 0
dgtl_dock_headset = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
usb_device = 0
usb_accessory = 0
wired_headset = 0
@@ -127,7 +127,7 @@
speaker = 0
hdmi = 0
dgtl_dock_headset = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
usb_device = 0
usb_accessory = 0
wired_headset = 0
@@ -145,7 +145,7 @@
speaker = 0
hdmi = 0
dgtl_dock_headset = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
usb_device = 0
usb_accessory = 0
wired_headset = 0
@@ -163,7 +163,7 @@
speaker = 0
hdmi = 0
dgtl_dock_headset = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
usb_device = 0
usb_accessory = 0
wired_headset = 1
@@ -181,7 +181,7 @@
speaker = 0
hdmi = 0
dgtl_dock_headset = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
usb_device = 0
usb_accessory = 1
wired_headset = 0
@@ -199,7 +199,7 @@
speaker = 0
hdmi = 0
dgtl_dock_headset = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
usb_device = 1
usb_accessory = 0
wired_headset = 0
@@ -217,7 +217,7 @@
speaker = 0
hdmi = 0
dgtl_dock_headset = 1
- angl_dock_headset = 0
+ anlg_dock_headset = 0
usb_device = 0
usb_accessory = 0
wired_headset = 0
@@ -235,7 +235,7 @@
speaker = 0
hdmi = 1
dgtl_dock_headset = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
usb_device = 0
usb_accessory = 0
wired_headset = 0
@@ -254,7 +254,7 @@
speaker = 0
hdmi = 0
dgtl_dock_headset = 0
- angl_dock_headset = 1
+ anlg_dock_headset = 1
usb_device = 0
usb_accessory = 0
wired_headset = 0
@@ -277,7 +277,7 @@
speaker = 1
hdmi = 0
dgtl_dock_headset = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
usb_device = 0
usb_accessory = 0
wired_headset = 0
@@ -293,7 +293,7 @@
speaker = 0
hdmi = 0
dgtl_dock_headset = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
usb_device = 0
usb_accessory = 0
wired_headset = 0
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_patch.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_patch.pfw
index d2cc090..feeeec6 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_patch.pfw
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_patch.pfw
@@ -14,7 +14,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_phone.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_phone.pfw
index 5693d4e..da2fc9b 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_phone.pfw
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_phone.pfw
@@ -32,7 +32,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -55,7 +55,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -78,7 +78,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -108,7 +108,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -138,7 +138,7 @@
bluetooth_a2dp_headphones = 1
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -168,7 +168,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 1
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -195,7 +195,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -222,7 +222,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -245,7 +245,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -283,7 +283,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 1
@@ -311,7 +311,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 1
@@ -339,7 +339,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 1
usb_accessory = 0
usb_device = 0
@@ -367,7 +367,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 1
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -395,7 +395,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 1
+ anlg_dock_headset = 1
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -422,7 +422,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -449,7 +449,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -472,7 +472,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_rerouting.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_rerouting.pfw
index 10f8814..3275cdf 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_rerouting.pfw
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_rerouting.pfw
@@ -14,7 +14,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_sonification.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_sonification.pfw
index c4edeeb..a60445b 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_sonification.pfw
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_sonification.pfw
@@ -69,7 +69,7 @@
bluetooth_a2dp = 1
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -95,7 +95,7 @@
bluetooth_a2dp = 0
bluetooth_a2dp_headphones = 1
bluetooth_a2dp_speaker = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -121,7 +121,7 @@
bluetooth_a2dp = 0
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 1
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -148,7 +148,7 @@
bluetooth_a2dp = 0
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -175,7 +175,7 @@
bluetooth_a2dp = 0
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -202,7 +202,7 @@
bluetooth_a2dp = 0
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -238,7 +238,7 @@
bluetooth_a2dp = 0
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -276,7 +276,7 @@
bluetooth_a2dp = 0
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -312,7 +312,7 @@
bluetooth_a2dp = 0
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -349,7 +349,7 @@
bluetooth_a2dp = 0
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 1
@@ -378,7 +378,7 @@
bluetooth_a2dp = 0
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 1
usb_device = 0
@@ -407,7 +407,7 @@
bluetooth_a2dp = 0
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 1
usb_accessory = 0
usb_device = 0
@@ -437,7 +437,7 @@
bluetooth_a2dp = 0
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
- angl_dock_headset = 1
+ anlg_dock_headset = 1
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -464,7 +464,7 @@
bluetooth_a2dp = 0
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -482,7 +482,7 @@
bluetooth_a2dp = 0
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_sonification_respectful.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_sonification_respectful.pfw
index 0a3dd5f..6b11e23 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_sonification_respectful.pfw
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_sonification_respectful.pfw
@@ -92,7 +92,7 @@
wired_headset = 0
wired_headphone = 0
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -119,7 +119,7 @@
wired_headset = 0
wired_headphone = 0
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -146,7 +146,7 @@
wired_headset = 0
wired_headphone = 0
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -173,7 +173,7 @@
wired_headset = 0
wired_headphone = 0
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -200,7 +200,7 @@
wired_headset = 0
wired_headphone = 0
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -227,7 +227,7 @@
wired_headset = 0
wired_headphone = 0
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -264,7 +264,7 @@
wired_headset = 0
wired_headphone = 1
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -305,7 +305,7 @@
wired_headset = 0
wired_headphone = 0
line = 1
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -342,7 +342,7 @@
wired_headset = 1
wired_headphone = 0
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -380,7 +380,7 @@
wired_headset = 0
wired_headphone = 0
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 1
@@ -410,7 +410,7 @@
wired_headset = 0
wired_headphone = 0
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 1
usb_device = 0
@@ -440,7 +440,7 @@
wired_headset = 0
wired_headphone = 0
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 1
usb_accessory = 0
usb_device = 0
@@ -470,7 +470,7 @@
wired_headset = 0
wired_headphone = 0
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -501,7 +501,7 @@
wired_headset = 0
wired_headphone = 0
line = 0
- angl_dock_headset = 1
+ anlg_dock_headset = 1
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -528,7 +528,7 @@
wired_headset = 0
wired_headphone = 0
line = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_transmitted_through_speaker.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_transmitted_through_speaker.pfw
index 3fc7670..418f3cc 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_transmitted_through_speaker.pfw
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_transmitted_through_speaker.pfw
@@ -19,7 +19,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/PolicyConfigurableDomains.xml b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/PolicyConfigurableDomains.xml
index 0710441..baffa81 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/PolicyConfigurableDomains.xml
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/PolicyConfigurableDomains.xml
@@ -145,7 +145,7 @@
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/speaker"/>
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/hdmi"/>
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/dgtl_dock_headset"/>
- <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/angl_dock_headset"/>
+ <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/anlg_dock_headset"/>
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/usb_device"/>
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/usb_accessory"/>
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/wired_headset"/>
@@ -167,8 +167,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/usb_device">
<BitParameter Name="usb_device">0</BitParameter>
@@ -208,8 +208,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/usb_device">
<BitParameter Name="usb_device">0</BitParameter>
@@ -249,8 +249,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/usb_device">
<BitParameter Name="usb_device">0</BitParameter>
@@ -290,8 +290,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/usb_device">
<BitParameter Name="usb_device">0</BitParameter>
@@ -331,8 +331,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/usb_device">
<BitParameter Name="usb_device">0</BitParameter>
@@ -372,8 +372,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/usb_device">
<BitParameter Name="usb_device">0</BitParameter>
@@ -413,8 +413,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/usb_device">
<BitParameter Name="usb_device">0</BitParameter>
@@ -454,8 +454,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/usb_device">
<BitParameter Name="usb_device">0</BitParameter>
@@ -495,8 +495,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/usb_device">
<BitParameter Name="usb_device">0</BitParameter>
@@ -536,8 +536,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/usb_device">
<BitParameter Name="usb_device">1</BitParameter>
@@ -577,8 +577,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">1</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/usb_device">
<BitParameter Name="usb_device">0</BitParameter>
@@ -618,8 +618,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/usb_device">
<BitParameter Name="usb_device">0</BitParameter>
@@ -659,8 +659,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">1</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">1</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/usb_device">
<BitParameter Name="usb_device">0</BitParameter>
@@ -700,8 +700,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/usb_device">
<BitParameter Name="usb_device">0</BitParameter>
@@ -741,8 +741,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/media/selected_output_devices/mask/usb_device">
<BitParameter Name="usb_device">0</BitParameter>
@@ -1039,7 +1039,7 @@
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/bluetooth_a2dp_headphones"/>
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/bluetooth_a2dp_speaker"/>
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/hdmi"/>
- <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/angl_dock_headset"/>
+ <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/anlg_dock_headset"/>
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/dgtl_dock_headset"/>
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/usb_accessory"/>
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/usb_device"/>
@@ -1079,8 +1079,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -1132,8 +1132,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -1185,8 +1185,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -1238,8 +1238,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -1291,8 +1291,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -1344,8 +1344,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -1397,8 +1397,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -1450,8 +1450,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -1503,8 +1503,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -1556,8 +1556,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -1609,8 +1609,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -1662,8 +1662,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">1</BitParameter>
@@ -1715,8 +1715,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">1</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -1768,8 +1768,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">1</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">1</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -1821,8 +1821,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -1874,8 +1874,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -1927,8 +1927,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/phone/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -2228,7 +2228,7 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/bluetooth_a2dp"/>
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/bluetooth_a2dp_headphones"/>
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/bluetooth_a2dp_speaker"/>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/angl_dock_headset"/>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/anlg_dock_headset"/>
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/dgtl_dock_headset"/>
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/usb_accessory"/>
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/usb_device"/>
@@ -2264,8 +2264,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/bluetooth_a2dp_speaker">
<BitParameter Name="bluetooth_a2dp_speaker">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -2311,8 +2311,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/bluetooth_a2dp_speaker">
<BitParameter Name="bluetooth_a2dp_speaker">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -2358,8 +2358,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/bluetooth_a2dp_speaker">
<BitParameter Name="bluetooth_a2dp_speaker">1</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -2405,8 +2405,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/bluetooth_a2dp_speaker">
<BitParameter Name="bluetooth_a2dp_speaker">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -2452,8 +2452,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/bluetooth_a2dp_speaker">
<BitParameter Name="bluetooth_a2dp_speaker">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -2499,8 +2499,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/bluetooth_a2dp_speaker">
<BitParameter Name="bluetooth_a2dp_speaker">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -2546,8 +2546,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/bluetooth_a2dp_speaker">
<BitParameter Name="bluetooth_a2dp_speaker">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -2593,8 +2593,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/bluetooth_a2dp_speaker">
<BitParameter Name="bluetooth_a2dp_speaker">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -2640,8 +2640,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/bluetooth_a2dp_speaker">
<BitParameter Name="bluetooth_a2dp_speaker">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -2687,8 +2687,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/bluetooth_a2dp_speaker">
<BitParameter Name="bluetooth_a2dp_speaker">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -2734,8 +2734,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/bluetooth_a2dp_speaker">
<BitParameter Name="bluetooth_a2dp_speaker">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -2781,8 +2781,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/bluetooth_a2dp_speaker">
<BitParameter Name="bluetooth_a2dp_speaker">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">1</BitParameter>
@@ -2828,8 +2828,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/bluetooth_a2dp_speaker">
<BitParameter Name="bluetooth_a2dp_speaker">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">1</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">1</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -2875,8 +2875,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/bluetooth_a2dp_speaker">
<BitParameter Name="bluetooth_a2dp_speaker">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -2922,8 +2922,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/bluetooth_a2dp_speaker">
<BitParameter Name="bluetooth_a2dp_speaker">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -3246,7 +3246,7 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/wired_headset"/>
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/wired_headphone"/>
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/line"/>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/angl_dock_headset"/>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/anlg_dock_headset"/>
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/dgtl_dock_headset"/>
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/usb_accessory"/>
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/usb_device"/>
@@ -3284,8 +3284,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -3331,8 +3331,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -3378,8 +3378,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -3425,8 +3425,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -3472,8 +3472,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -3519,8 +3519,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -3566,8 +3566,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -3613,8 +3613,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/line">
<BitParameter Name="line">1</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -3660,8 +3660,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -3707,8 +3707,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -3754,8 +3754,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -3801,8 +3801,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">1</BitParameter>
@@ -3848,8 +3848,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -3895,8 +3895,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">1</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">1</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -3942,8 +3942,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/sonification_respectful/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -4207,7 +4207,7 @@
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/bluetooth_a2dp_headphones"/>
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/bluetooth_a2dp_speaker"/>
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/hdmi"/>
- <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/angl_dock_headset"/>
+ <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/anlg_dock_headset"/>
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/dgtl_dock_headset"/>
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/usb_accessory"/>
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/usb_device"/>
@@ -4247,8 +4247,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -4300,8 +4300,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -4353,8 +4353,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -4406,8 +4406,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -4459,8 +4459,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -4512,8 +4512,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -4565,8 +4565,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -4618,8 +4618,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -4671,8 +4671,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -4724,8 +4724,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -4777,8 +4777,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -4830,8 +4830,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -4883,8 +4883,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">1</BitParameter>
@@ -4936,8 +4936,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">1</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -4989,8 +4989,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">1</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">1</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -5042,8 +5042,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -5095,8 +5095,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -5148,8 +5148,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/dtmf/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -5448,7 +5448,7 @@
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/bluetooth_a2dp_headphones"/>
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/bluetooth_a2dp_speaker"/>
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/hdmi"/>
- <ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/angl_dock_headset"/>
+ <ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/anlg_dock_headset"/>
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/dgtl_dock_headset"/>
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/usb_accessory"/>
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/usb_device"/>
@@ -5490,8 +5490,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -5543,8 +5543,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -5596,8 +5596,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -5649,8 +5649,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -5702,8 +5702,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -5755,8 +5755,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -5808,8 +5808,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -5861,8 +5861,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -5914,8 +5914,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -5967,8 +5967,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">1</BitParameter>
@@ -6020,8 +6020,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">1</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -6073,8 +6073,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">1</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">1</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -6126,8 +6126,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/enforced_audible/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -6170,7 +6170,7 @@
<ConfigurableElement Path="/Policy/policy/strategies/transmitted_through_speaker/selected_output_devices/mask/bluetooth_a2dp_headphones"/>
<ConfigurableElement Path="/Policy/policy/strategies/transmitted_through_speaker/selected_output_devices/mask/bluetooth_a2dp_speaker"/>
<ConfigurableElement Path="/Policy/policy/strategies/transmitted_through_speaker/selected_output_devices/mask/hdmi"/>
- <ConfigurableElement Path="/Policy/policy/strategies/transmitted_through_speaker/selected_output_devices/mask/angl_dock_headset"/>
+ <ConfigurableElement Path="/Policy/policy/strategies/transmitted_through_speaker/selected_output_devices/mask/anlg_dock_headset"/>
<ConfigurableElement Path="/Policy/policy/strategies/transmitted_through_speaker/selected_output_devices/mask/dgtl_dock_headset"/>
<ConfigurableElement Path="/Policy/policy/strategies/transmitted_through_speaker/selected_output_devices/mask/usb_accessory"/>
<ConfigurableElement Path="/Policy/policy/strategies/transmitted_through_speaker/selected_output_devices/mask/usb_device"/>
@@ -6230,8 +6230,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/transmitted_through_speaker/selected_output_devices/mask/hdmi">
<BitParameter Name="hdmi">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/transmitted_through_speaker/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/transmitted_through_speaker/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/transmitted_through_speaker/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -6539,7 +6539,7 @@
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/wired_headset"/>
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/wired_headphone"/>
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/line"/>
- <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/angl_dock_headset"/>
+ <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/anlg_dock_headset"/>
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/dgtl_dock_headset"/>
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/usb_accessory"/>
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/usb_device"/>
@@ -6583,8 +6583,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -6636,8 +6636,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -6689,8 +6689,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -6742,8 +6742,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -6795,8 +6795,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -6848,8 +6848,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -6901,8 +6901,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -6954,8 +6954,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -7007,8 +7007,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -7060,8 +7060,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/line">
<BitParameter Name="line">1</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -7113,8 +7113,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -7166,8 +7166,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -7219,8 +7219,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -7272,8 +7272,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">1</BitParameter>
@@ -7325,8 +7325,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -7378,8 +7378,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">1</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">1</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -7431,8 +7431,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -7484,8 +7484,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -7537,8 +7537,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/accessibility/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -7710,7 +7710,7 @@
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/wired_headset"/>
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/wired_headphone"/>
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/line"/>
- <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/angl_dock_headset"/>
+ <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/anlg_dock_headset"/>
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/dgtl_dock_headset"/>
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/usb_accessory"/>
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/usb_device"/>
@@ -7742,8 +7742,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -7783,8 +7783,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -7824,8 +7824,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -7865,8 +7865,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -7906,8 +7906,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -7947,8 +7947,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -7988,8 +7988,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/line">
<BitParameter Name="line">1</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -8029,8 +8029,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -8070,8 +8070,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -8111,8 +8111,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -8152,8 +8152,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">1</BitParameter>
@@ -8193,8 +8193,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -8234,8 +8234,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">1</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">1</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -8275,8 +8275,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
@@ -8316,8 +8316,8 @@
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/line">
<BitParameter Name="line">0</BitParameter>
</ConfigurableElement>
- <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/angl_dock_headset">
- <BitParameter Name="angl_dock_headset">0</BitParameter>
+ <ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/anlg_dock_headset">
+ <BitParameter Name="anlg_dock_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/strategies/rerouting/selected_output_devices/mask/dgtl_dock_headset">
<BitParameter Name="dgtl_dock_headset">0</BitParameter>
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/volumes.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/volumes.pfw
index 7db4537..cf1857e 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/volumes.pfw
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/volumes.pfw
@@ -11,6 +11,7 @@
/Policy/policy/streams/enforced_audible/applicable_volume_profile/volume_profile = enforced_audible
/Policy/policy/streams/tts/applicable_volume_profile/volume_profile = tts
/Policy/policy/streams/accessibility/applicable_volume_profile/volume_profile = accessibility
+ /Policy/policy/streams/assistant/applicable_volume_profile/volume_profile = assistant
/Policy/policy/streams/rerouting/applicable_volume_profile/volume_profile = rerouting
/Policy/policy/streams/patch/applicable_volume_profile/volume_profile = patch
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/SettingsNoInput/Android.bp b/services/audiopolicy/engineconfigurable/parameter-framework/examples/SettingsNoInput/Android.bp
index ffd494e..9abcb70 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/SettingsNoInput/Android.bp
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/SettingsNoInput/Android.bp
@@ -55,7 +55,7 @@
srcs: [
":PolicyClass",
":PolicySubsystem",
- ":PolicySubsystem-CommonTypes",
+ ":buildcommontypesstructure_gen",
],
}
filegroup {
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/SettingsNoOutput/Android.bp b/services/audiopolicy/engineconfigurable/parameter-framework/examples/SettingsNoOutput/Android.bp
index 6fca048..27172a4 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/SettingsNoOutput/Android.bp
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/SettingsNoOutput/Android.bp
@@ -54,7 +54,7 @@
srcs: [
":PolicyClass",
":PolicySubsystem",
- ":PolicySubsystem-CommonTypes",
+ ":buildcommontypesstructure_gen",
],
}
filegroup {
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/SettingsNoOutput/device_for_strategies.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/SettingsNoOutput/device_for_strategies.pfw
index f923610..e259c00 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/SettingsNoOutput/device_for_strategies.pfw
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/SettingsNoOutput/device_for_strategies.pfw
@@ -13,7 +13,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -41,7 +41,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -69,7 +69,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -97,7 +97,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -125,7 +125,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -153,7 +153,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -181,7 +181,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -209,7 +209,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
@@ -237,7 +237,7 @@
bluetooth_a2dp_headphones = 0
bluetooth_a2dp_speaker = 0
hdmi = 0
- angl_dock_headset = 0
+ anlg_dock_headset = 0
dgtl_dock_headset = 0
usb_accessory = 0
usb_device = 0
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem-CommonTypes.xml b/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem-CommonTypes.xml
deleted file mode 100644
index d17c021..0000000
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem-CommonTypes.xml
+++ /dev/null
@@ -1,186 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ComponentTypeSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:xi="http://www.w3.org/2001/XInclude"
- xsi:noNamespaceSchemaLocation="Schemas/ComponentTypeSet.xsd">
- <!-- Output devices definition as a bitfield for the supported devices per output
- profile. It must match with the output device enum parameter.
- -->
- <!--#################### GLOBAL COMPONENTS BEGIN ####################-->
-
- <!--#################### GLOBAL COMPONENTS END ####################-->
-
- <ComponentType Name="OutputDevicesMask" Description="32th bit is not allowed as dedicated
- for input devices detection">
- <BitParameterBlock Name="mask" Size="32">
- <BitParameter Name="earpiece" Size="1" Pos="0"/>
- <BitParameter Name="speaker" Size="1" Pos="1"/>
- <BitParameter Name="wired_headset" Size="1" Pos="2"/>
- <BitParameter Name="wired_headphone" Size="1" Pos="3"/>
- <BitParameter Name="bluetooth_sco" Size="1" Pos="4"/>
- <BitParameter Name="bluetooth_sco_headset" Size="1" Pos="5"/>
- <BitParameter Name="bluetooth_sco_carkit" Size="1" Pos="6"/>
- <BitParameter Name="bluetooth_a2dp" Size="1" Pos="7"/>
- <BitParameter Name="bluetooth_a2dp_headphones" Size="1" Pos="8"/>
- <BitParameter Name="bluetooth_a2dp_speaker" Size="1" Pos="9"/>
- <BitParameter Name="hdmi" Size="1" Pos="10"/>
- <BitParameter Name="angl_dock_headset" Size="1" Pos="11"/>
- <BitParameter Name="dgtl_dock_headset" Size="1" Pos="12"/>
- <BitParameter Name="usb_accessory" Size="1" Pos="13"/>
- <BitParameter Name="usb_device" Size="1" Pos="14"/>
- <BitParameter Name="remote_submix" Size="1" Pos="15"/>
- <BitParameter Name="telephony_tx" Size="1" Pos="16"/>
- <BitParameter Name="line" Size="1" Pos="17"/>
- <BitParameter Name="hdmi_arc" Size="1" Pos="18"/>
- <BitParameter Name="spdif" Size="1" Pos="19"/>
- <BitParameter Name="fm" Size="1" Pos="20"/>
- <BitParameter Name="aux_line" Size="1" Pos="21"/>
- <BitParameter Name="speaker_safe" Size="1" Pos="22"/>
- <BitParameter Name="ip" Size="1" Pos="23"/>
- <BitParameter Name="bus" Size="1" Pos="24"/>
- <BitParameter Name="proxy" Size="1" Pos="25"/>
- <BitParameter Name="usb_headset" Size="1" Pos="26"/>
- <BitParameter Name="hearing_aid" Size="1" Pos="27"/>
- <BitParameter Name="echo_canceller" Size="1" Pos="28"/>
- <BitParameter Name="stub" Size="1" Pos="30"/>
- </BitParameterBlock>
- </ComponentType>
-
- <!-- Input devices definition as a bitfield for the supported devices per Input
- profile. It must match with the Input device enum parameter.
- -->
- <ComponentType Name="InputDevicesMask">
- <BitParameterBlock Name="mask" Size="32">
- <BitParameter Name="communication" Size="1" Pos="0"/>
- <BitParameter Name="ambient" Size="1" Pos="1"/>
- <BitParameter Name="builtin_mic" Size="1" Pos="2"/>
- <BitParameter Name="bluetooth_sco_headset" Size="1" Pos="3"/>
- <BitParameter Name="wired_headset" Size="1" Pos="4"/>
- <BitParameter Name="hdmi" Size="1" Pos="5"/>
- <BitParameter Name="telephony_rx" Size="1" Pos="6"/>
- <BitParameter Name="back_mic" Size="1" Pos="7"/>
- <BitParameter Name="remote_submix" Size="1" Pos="8"/>
- <BitParameter Name="anlg_dock_headset" Size="1" Pos="9"/>
- <BitParameter Name="dgtl_dock_headset" Size="1" Pos="10"/>
- <BitParameter Name="usb_accessory" Size="1" Pos="11"/>
- <BitParameter Name="usb_device" Size="1" Pos="12"/>
- <BitParameter Name="fm_tuner" Size="1" Pos="13"/>
- <BitParameter Name="tv_tuner" Size="1" Pos="14"/>
- <BitParameter Name="line" Size="1" Pos="15"/>
- <BitParameter Name="spdif" Size="1" Pos="16"/>
- <BitParameter Name="bluetooth_a2dp" Size="1" Pos="17"/>
- <BitParameter Name="loopback" Size="1" Pos="18"/>
- <BitParameter Name="ip" Size="1" Pos="19"/>
- <BitParameter Name="bus" Size="1" Pos="20"/>
- <BitParameter Name="proxy" Size="1" Pos="21"/>
- <BitParameter Name="usb_headset" Size="1" Pos="22"/>
- <BitParameter Name="bluetooth_ble" Size="1" Pos="23"/>
- <BitParameter Name="hdmi_arc" Size="1" Pos="24"/>
- <BitParameter Name="echo_reference" Size="1" Pos="25"/>
- <BitParameter Name="stub" Size="1" Pos="30"/>
- </BitParameterBlock>
- </ComponentType>
-
- <ComponentType Name="OutputFlags"
- Description="the audio output flags serve two purposes:
- - when an AudioTrack is created they indicate a wish to be connected to an
- output stream with attributes corresponding to the specified flags.
- - when present in an output profile descriptor listed for a particular audio
- hardware module, they indicate that an output stream can be opened that
- supports the attributes indicated by the flags.
- The audio policy manager will try to match the flags in the request
- (when getOuput() is called) to an available output stream.">
- <BitParameterBlock Name="mask" Size="32">
- <BitParameter Name="direct" Size="1" Pos="0"/>
- <BitParameter Name="primary" Size="1" Pos="1"/>
- <BitParameter Name="fast" Size="1" Pos="2"/>
- <BitParameter Name="deep_buffer" Size="1" Pos="3"/>
- <BitParameter Name="compress_offload" Size="1" Pos="4"/>
- <BitParameter Name="non_blocking" Size="1" Pos="5"/>
- <BitParameter Name="hw_av_sync" Size="1" Pos="6"/>
- <BitParameter Name="tts" Size="1" Pos="7"/>
- <BitParameter Name="raw" Size="1" Pos="8"/>
- <BitParameter Name="sync" Size="1" Pos="9"/>
- <BitParameter Name="iec958_nonaudio" Size="1" Pos="10"/>
- </BitParameterBlock>
- </ComponentType>
-
- <ComponentType Name="InputFlags"
- Description="The audio input flags are analogous to audio output flags.
- Currently they are used only when an AudioRecord is created,
- to indicate a preference to be connected to an input stream with
- attributes corresponding to the specified flags.">
- <BitParameterBlock Name="mask" Size="32">
- <BitParameter Name="fast" Size="1" Pos="0"/>
- <BitParameter Name="hw_hotword" Size="1" Pos="2"/>
- <BitParameter Name="raw" Size="1" Pos="3"/>
- <BitParameter Name="sync" Size="1" Pos="4"/>
- </BitParameterBlock>
- </ComponentType>
-
- <ComponentType Name="InputSourcesMask" Description="The audio input source is also known
- as the use case.">
- <BitParameterBlock Name="mask" Size="32">
- <BitParameter Name="default" Size="1" Pos="0"/>
- <BitParameter Name="mic" Size="1" Pos="1"/>
- <BitParameter Name="voice_uplink" Size="1" Pos="2"/>
- <BitParameter Name="voice_downlink" Size="1" Pos="3"/>
- <BitParameter Name="voice_call" Size="1" Pos="4"/>
- <BitParameter Name="camcorder" Size="1" Pos="5"/>
- <BitParameter Name="voice_recognition" Size="1" Pos="6"/>
- <BitParameter Name="voice_communication" Size="1" Pos="7"/>
- <BitParameter Name="remote_submix" Size="1" Pos="8"/>
- <BitParameter Name="unprocessed" Size="1" Pos="9"/>
- <BitParameter Name="voice_performance" Size="1" Pos="10"/>
- <BitParameter Name="echo_reference" Size="1" Pos="11"/>
- <BitParameter Name="fm_tuner" Size="1" Pos="12"/>
- <BitParameter Name="hotword" Size="1" Pos="13"/>
- </BitParameterBlock>
- </ComponentType>
-
- <!--#################### STREAM COMMON TYPES BEGIN ####################-->
-
- <ComponentType Name="VolumeProfileType">
- <EnumParameter Name="volume_profile" Size="32">
- <ValuePair Literal="voice_call" Numerical="0"/>
- <ValuePair Literal="system" Numerical="1"/>
- <ValuePair Literal="ring" Numerical="2"/>
- <ValuePair Literal="music" Numerical="3"/>
- <ValuePair Literal="alarm" Numerical="4"/>
- <ValuePair Literal="notification" Numerical="5"/>
- <ValuePair Literal="bluetooth_sco" Numerical="6"/>
- <ValuePair Literal="enforced_audible" Numerical="7"/>
- <ValuePair Literal="dtmf" Numerical="8"/>
- <ValuePair Literal="tts" Numerical="9"/>
- <ValuePair Literal="accessibility" Numerical="10"/>
- <ValuePair Literal="rerouting" Numerical="11"/>
- <ValuePair Literal="patch" Numerical="12"/>
- </EnumParameter>
- </ComponentType>
-
- <ComponentType Name="Stream" Mapping="Stream">
- <Component Name="applicable_volume_profile" Type="VolumeProfileType"
- Description="Volume profile followed by a given stream type."/>
- </ComponentType>
-
- <!--#################### STREAM COMMON TYPES END ####################-->
-
- <!--#################### INPUT SOURCE COMMON TYPES BEGIN ####################-->
-
- <ComponentType Name="InputSource">
- <Component Name="applicable_input_device" Type="InputDevicesMask"
- Mapping="InputSource" Description="Selected Input device"/>
- </ComponentType>
-
- <!--#################### INPUT SOURCE COMMON TYPES END ####################-->
-
- <!--#################### PRODUCT STRATEGY COMMON TYPES BEGIN ####################-->
-
- <ComponentType Name="ProductStrategy" Mapping="ProductStrategy">
- <Component Name="selected_output_devices" Type="OutputDevicesMask"/>
- <StringParameter Name="device_address" MaxLength="256"
- Description="if any, device address associated"/>
- </ComponentType>
-
- <!--#################### PRODUCT STRATEGY COMMON TYPES END ####################-->
-
-</ComponentTypeSet>
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem-CommonTypes.xml.in b/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem-CommonTypes.xml.in
new file mode 100644
index 0000000..2e9f37e
--- /dev/null
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem-CommonTypes.xml.in
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ComponentTypeSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ xsi:noNamespaceSchemaLocation="Schemas/ComponentTypeSet.xsd">
+ <!-- Output devices definition as a bitfield for the supported devices per output
+ profile. It must match with the output device enum parameter.
+ -->
+ <!--#################### GLOBAL COMPONENTS BEGIN ####################-->
+
+ <!--#################### GLOBAL COMPONENTS END ####################-->
+
+ <!-- Automatically filled from audio-base.h file -->
+ <ComponentType Name="OutputDevicesMask" Description="32th bit is not allowed as dedicated for input devices detection">
+ <BitParameterBlock Name="mask" Size="32">
+ </BitParameterBlock>
+ </ComponentType>
+
+ <!-- Input devices definition as a bitfield for the supported devices per Input
+ profile. It must match with the Input device enum parameter.
+ -->
+ <!-- Automatically filled from audio-base.h file -->
+ <ComponentType Name="InputDevicesMask">
+ <BitParameterBlock Name="mask" Size="32">
+ </BitParameterBlock>
+ </ComponentType>
+
+ <!--#################### STREAM COMMON TYPES BEGIN ####################-->
+ <!-- Automatically filled from audio-base.h file. VolumeProfileType is associated to stream type -->
+ <ComponentType Name="VolumeProfileType">
+ <EnumParameter Name="volume_profile" Size="32">
+ </EnumParameter>
+ </ComponentType>
+
+ <ComponentType Name="Stream" Mapping="Stream">
+ <Component Name="applicable_volume_profile" Type="VolumeProfileType"
+ Description="Volume profile followed by a given stream type."/>
+ </ComponentType>
+
+ <!--#################### STREAM COMMON TYPES END ####################-->
+
+ <!--#################### INPUT SOURCE COMMON TYPES BEGIN ####################-->
+
+ <ComponentType Name="InputSource">
+ <Component Name="applicable_input_device" Type="InputDevicesMask"
+ Mapping="InputSource" Description="Selected Input device"/>
+ </ComponentType>
+
+ <!--#################### INPUT SOURCE COMMON TYPES END ####################-->
+
+ <!--#################### PRODUCT STRATEGY COMMON TYPES BEGIN ####################-->
+
+ <ComponentType Name="ProductStrategy" Mapping="ProductStrategy">
+ <Component Name="selected_output_devices" Type="OutputDevicesMask"/>
+ <StringParameter Name="device_address" MaxLength="256"
+ Description="if any, device address associated"/>
+ </ComponentType>
+
+ <!--#################### PRODUCT STRATEGY COMMON TYPES END ####################-->
+
+</ComponentTypeSet>
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem-no-strategy.xml b/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem-no-strategy.xml
index a4e7537..ed349c8 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem-no-strategy.xml
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem-no-strategy.xml
@@ -28,6 +28,8 @@
Description="Transmitted Through Speaker. Plays over speaker only, silent on other devices"/>
<Component Name="accessibility" Type="Stream" Mapping="Name:AUDIO_STREAM_ACCESSIBILITY"
Description="For accessibility talk back prompts"/>
+ <Component Name="assistant" Type="Stream" Mapping="Name:AUDIO_STREAM_ASSISTANT"
+ Description="used by a virtual assistant like Google Assistant, Bixby, etc."/>
<Component Name="rerouting" Type="Stream" Mapping="Name:AUDIO_STREAM_REROUTING"
Description="For dynamic policy output mixes"/>
<Component Name="patch" Type="Stream" Mapping="Name:AUDIO_STREAM_PATCH"
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem.xml b/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem.xml
index 585ce87..7bbb57a 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem.xml
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem.xml
@@ -44,6 +44,8 @@
Description="Transmitted Through Speaker. Plays over speaker only, silent on other devices"/>
<Component Name="accessibility" Type="Stream" Mapping="Name:AUDIO_STREAM_ACCESSIBILITY"
Description="For accessibility talk back prompts"/>
+ <Component Name="assistant" Type="Stream" Mapping="Name:AUDIO_STREAM_ASSISTANT"
+ Description="used by a virtual assistant like Google Assistant, Bixby, etc."/>
<Component Name="rerouting" Type="Stream" Mapping="Name:AUDIO_STREAM_REROUTING"
Description="For dynamic policy output mixes"/>
<Component Name="patch" Type="Stream" Mapping="Name:AUDIO_STREAM_PATCH"
diff --git a/services/audiopolicy/engineconfigurable/src/Engine.cpp b/services/audiopolicy/engineconfigurable/src/Engine.cpp
index 752ba92..6d42fcf 100644
--- a/services/audiopolicy/engineconfigurable/src/Engine.cpp
+++ b/services/audiopolicy/engineconfigurable/src/Engine.cpp
@@ -80,8 +80,9 @@
status_t Engine::initCheck()
{
- if (mPolicyParameterMgr == nullptr || mPolicyParameterMgr->start() != NO_ERROR) {
- ALOGE("%s: could not start Policy PFW", __FUNCTION__);
+ std::string error;
+ if (mPolicyParameterMgr == nullptr || mPolicyParameterMgr->start(error) != NO_ERROR) {
+ ALOGE("%s: could not start Policy PFW: %s", __FUNCTION__, error.c_str());
return NO_INIT;
}
return EngineBase::initCheck();
@@ -128,7 +129,7 @@
Element<Key> *element = getFromCollection<Key>(key);
if (element == NULL) {
ALOGE("%s: Element not found within collection", __FUNCTION__);
- return BAD_VALUE;
+ return false;
}
return element->template set<Property>(property) == NO_ERROR;
}
@@ -162,21 +163,21 @@
return mPolicyParameterMgr->getForceUse(usage);
}
-status_t Engine::setDeviceConnectionState(const sp<DeviceDescriptor> devDesc,
+status_t Engine::setDeviceConnectionState(const sp<DeviceDescriptor> device,
audio_policy_dev_state_t state)
{
- mPolicyParameterMgr->setDeviceConnectionState(devDesc, state);
-
- if (audio_is_output_device(devDesc->type())) {
+ mPolicyParameterMgr->setDeviceConnectionState(
+ device->type(), device->address().c_str(), state);
+ if (audio_is_output_device(device->type())) {
// FIXME: Use DeviceTypeSet when the interface is ready
return mPolicyParameterMgr->setAvailableOutputDevices(
deviceTypesToBitMask(getApmObserver()->getAvailableOutputDevices().types()));
- } else if (audio_is_input_device(devDesc->type())) {
+ } else if (audio_is_input_device(device->type())) {
// FIXME: Use DeviceTypeSet when the interface is ready
return mPolicyParameterMgr->setAvailableInputDevices(
deviceTypesToBitMask(getApmObserver()->getAvailableInputDevices().types()));
}
- return EngineBase::setDeviceConnectionState(devDesc, state);
+ return EngineBase::setDeviceConnectionState(device, state);
}
status_t Engine::loadAudioPolicyEngineConfig()
diff --git a/services/audiopolicy/engineconfigurable/src/InputSource.cpp b/services/audiopolicy/engineconfigurable/src/InputSource.cpp
index d252d3f..aa06ae3 100644
--- a/services/audiopolicy/engineconfigurable/src/InputSource.cpp
+++ b/services/audiopolicy/engineconfigurable/src/InputSource.cpp
@@ -30,7 +30,7 @@
return BAD_VALUE;
}
mIdentifier = identifier;
- ALOGD("%s: InputSource %s identifier 0x%X", __FUNCTION__, getName().c_str(), identifier);
+ ALOGV("%s: InputSource %s identifier 0x%X", __FUNCTION__, getName().c_str(), identifier);
return NO_ERROR;
}
@@ -46,15 +46,18 @@
template <>
status_t Element<audio_source_t>::set(audio_devices_t devices)
{
- if (devices != AUDIO_DEVICE_NONE) {
- devices |= AUDIO_DEVICE_BIT_IN;
+ if (devices == AUDIO_DEVICE_NONE) {
+ // Reset
+ mApplicableDevices = devices;
+ return NO_ERROR;
}
+ devices |= AUDIO_DEVICE_BIT_IN;
if (!audio_is_input_device(devices)) {
ALOGE("%s: trying to set an invalid device 0x%X for input source %s",
__FUNCTION__, devices, getName().c_str());
return BAD_VALUE;
}
- ALOGD("%s: 0x%X for input source %s", __FUNCTION__, devices, getName().c_str());
+ ALOGV("%s: 0x%X for input source %s", __FUNCTION__, devices, getName().c_str());
mApplicableDevices = devices;
return NO_ERROR;
}
diff --git a/services/audiopolicy/engineconfigurable/src/InputSource.h b/services/audiopolicy/engineconfigurable/src/InputSource.h
index e1865cc..d64a60a 100644
--- a/services/audiopolicy/engineconfigurable/src/InputSource.h
+++ b/services/audiopolicy/engineconfigurable/src/InputSource.h
@@ -73,10 +73,11 @@
Element(const Element &object);
Element &operator=(const Element &object);
- std::string mName; /**< Unique literal Identifier of a policy base element*/
- audio_source_t mIdentifier; /**< Unique numerical Identifier of a policy base element*/
-
- audio_devices_t mApplicableDevices; /**< Applicable input device for this input source. */
+ const std::string mName; /**< Unique literal Identifier of a policy base element*/
+ /** Unique numerical Identifier of a policy base element */
+ audio_source_t mIdentifier = AUDIO_SOURCE_DEFAULT;
+ /** Applicable input device for this input source. */
+ audio_devices_t mApplicableDevices = AUDIO_DEVICE_NONE;
};
typedef Element<audio_source_t> InputSource;
diff --git a/services/audiopolicy/engineconfigurable/src/Stream.cpp b/services/audiopolicy/engineconfigurable/src/Stream.cpp
index 297eb02..e64ba4b 100644
--- a/services/audiopolicy/engineconfigurable/src/Stream.cpp
+++ b/services/audiopolicy/engineconfigurable/src/Stream.cpp
@@ -30,7 +30,7 @@
return BAD_VALUE;
}
mIdentifier = identifier;
- ALOGD("%s: Stream %s identifier 0x%X", __FUNCTION__, getName().c_str(), identifier);
+ ALOGV("%s: Stream %s identifier 0x%X", __FUNCTION__, getName().c_str(), identifier);
return NO_ERROR;
}
@@ -41,7 +41,7 @@
return BAD_VALUE;
}
mVolumeProfile = volumeProfile;
- ALOGD("%s: 0x%X for Stream %s", __FUNCTION__, mVolumeProfile, getName().c_str());
+ ALOGV("%s: 0x%X for Stream %s", __FUNCTION__, mVolumeProfile, getName().c_str());
return NO_ERROR;
}
diff --git a/services/audiopolicy/engineconfigurable/tools/Android.bp b/services/audiopolicy/engineconfigurable/tools/Android.bp
index d9e97af..3e47324 100644
--- a/services/audiopolicy/engineconfigurable/tools/Android.bp
+++ b/services/audiopolicy/engineconfigurable/tools/Android.bp
@@ -133,3 +133,29 @@
],
out: ["ProductStrategies.xml"],
}
+
+//##################################################################################################
+// Tools for policy parameter-framework common type structure file generation
+//
+python_binary_host {
+ name: "buildCommonTypesStructureFile.py",
+ main: "buildCommonTypesStructureFile.py",
+ srcs: [
+ "buildCommonTypesStructureFile.py",
+ ],
+ defaults: ["tools_default"],
+}
+
+genrule_defaults {
+ name: "buildcommontypesstructurerule",
+ tools: ["buildCommonTypesStructureFile.py"],
+ cmd: "$(location buildCommonTypesStructureFile.py) " +
+ "--androidaudiobaseheader $(location :libaudio_system_audio_base) " +
+ "--commontypesstructure $(location :common_types_structure_template) " +
+ "--outputfile $(out)",
+ srcs: [
+ ":common_types_structure_template",
+ ":libaudio_system_audio_base",
+ ],
+ out: ["PolicySubsystem-CommonTypes.xml"],
+}
diff --git a/services/audiopolicy/engineconfigurable/tools/buildCommonTypesStructureFile.py b/services/audiopolicy/engineconfigurable/tools/buildCommonTypesStructureFile.py
new file mode 100755
index 0000000..9a7fa8f
--- /dev/null
+++ b/services/audiopolicy/engineconfigurable/tools/buildCommonTypesStructureFile.py
@@ -0,0 +1,184 @@
+#! /usr/bin/python3
+#
+# pylint: disable=line-too-long, missing-docstring, logging-format-interpolation, invalid-name
+
+#
+# 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.
+
+import argparse
+import re
+import sys
+import os
+import logging
+import xml.etree.ElementTree as ET
+from collections import OrderedDict
+import xml.dom.minidom as MINIDOM
+
+def parseArgs():
+ argparser = argparse.ArgumentParser(description="Parameter-Framework XML \
+ structure file generator.\n\
+ Exit with the number of (recoverable or not) error that occured.")
+ argparser.add_argument('--androidaudiobaseheader',
+ help="Android Audio Base C header file, Mandatory.",
+ metavar="ANDROID_AUDIO_BASE_HEADER",
+ type=argparse.FileType('r'),
+ required=True)
+ argparser.add_argument('--commontypesstructure',
+ help="Structure XML base file. Mandatory.",
+ metavar="STRUCTURE_FILE_IN",
+ type=argparse.FileType('r'),
+ required=True)
+ argparser.add_argument('--outputfile',
+ help="Structure XML file. Mandatory.",
+ metavar="STRUCTURE_FILE_OUT",
+ type=argparse.FileType('w'),
+ required=True)
+ argparser.add_argument('--verbose',
+ action='store_true')
+
+ return argparser.parse_args()
+
+
+def findBitPos(decimal):
+ pos = 0
+ i = 1
+ while i != decimal:
+ i = i << 1
+ pos = pos + 1
+ if pos == 32:
+ return -1
+ return pos
+
+
+def generateXmlStructureFile(componentTypeDict, structureTypesFile, outputFile):
+
+ logging.info("Importing structureTypesFile {}".format(structureTypesFile))
+ component_types_in_tree = ET.parse(structureTypesFile)
+
+ component_types_root = component_types_in_tree.getroot()
+
+ for component_types_name, values_dict in componentTypeDict.items():
+ for component_type in component_types_root.findall('ComponentType'):
+ if component_type.get('Name') == component_types_name:
+ bitparameters_node = component_type.find("BitParameterBlock")
+ if bitparameters_node is not None:
+ ordered_values = OrderedDict(sorted(values_dict.items(), key=lambda x: x[1]))
+ for key, value in ordered_values.items():
+ value_node = ET.SubElement(bitparameters_node, "BitParameter")
+ value_node.set('Name', key)
+ value_node.set('Size', "1")
+ value_node.set('Pos', str(findBitPos(value)))
+
+ enum_parameter_node = component_type.find("EnumParameter")
+ if enum_parameter_node is not None:
+ ordered_values = OrderedDict(sorted(values_dict.items(), key=lambda x: x[1]))
+ for key, value in ordered_values.items():
+ value_node = ET.SubElement(enum_parameter_node, "ValuePair")
+ value_node.set('Literal', key)
+ value_node.set('Numerical', str(value))
+
+ xmlstr = ET.tostring(component_types_root, encoding='utf8', method='xml')
+ reparsed = MINIDOM.parseString(xmlstr)
+ prettyXmlStr = reparsed.toprettyxml(indent=" ", newl='\n')
+ prettyXmlStr = os.linesep.join([s for s in prettyXmlStr.splitlines() if s.strip()])
+ outputFile.write(prettyXmlStr)
+
+
+def capitalizeLine(line):
+ return ' '.join((w.capitalize() for w in line.split(' ')))
+
+def parseAndroidAudioFile(androidaudiobaseheaderFile):
+ #
+ # Adaptation table between Android Enumeration prefix and Audio PFW Criterion type names
+ #
+ component_type_mapping_table = {
+ 'AUDIO_STREAM' : "VolumeProfileType",
+ 'AUDIO_DEVICE_OUT' : "OutputDevicesMask",
+ 'AUDIO_DEVICE_IN' : "InputDevicesMask"}
+
+ all_component_types = {
+ 'VolumeProfileType' : {},
+ 'OutputDevicesMask' : {},
+ 'InputDevicesMask' : {}
+ }
+
+ #
+ # _CNT, _MAX, _ALL and _NONE are prohibited values as ther are just helpers for enum users.
+ #
+ ignored_values = ['CNT', 'MAX', 'ALL', 'NONE']
+
+ criteria_pattern = re.compile(
+ r"\s*(?P<type>(?:"+'|'.join(component_type_mapping_table.keys()) + "))_" \
+ r"(?P<literal>(?!" + '|'.join(ignored_values) + ")\w*)\s*=\s*" \
+ r"(?P<values>(?:0[xX])?[0-9a-fA-F]+)")
+
+ logging.info("Checking Android Header file {}".format(androidaudiobaseheaderFile))
+
+ for line_number, line in enumerate(androidaudiobaseheaderFile):
+ match = criteria_pattern.match(line)
+ if match:
+ logging.debug("The following line is VALID: {}:{}\n{}".format(
+ androidaudiobaseheaderFile.name, line_number, line))
+
+ component_type_name = component_type_mapping_table[match.groupdict()['type']]
+ component_type_literal = match.groupdict()['literal'].lower()
+
+ component_type_numerical_value = match.groupdict()['values']
+
+ # for AUDIO_DEVICE_IN: need to remove sign bit / rename default to stub
+ if component_type_name == "InputDevicesMask":
+ component_type_numerical_value = str(int(component_type_numerical_value, 0) & ~2147483648)
+ if component_type_literal == "default":
+ component_type_literal = "stub"
+
+ if component_type_name == "OutputDevicesMask":
+ if component_type_literal == "default":
+ component_type_literal = "stub"
+
+ # Remove duplicated numerical values
+ if int(component_type_numerical_value, 0) in all_component_types[component_type_name].values():
+ logging.info("The value {}:{} is duplicated for criterion {}, KEEPING LATEST".format(component_type_numerical_value, component_type_literal, component_type_name))
+ for key in list(all_component_types[component_type_name]):
+ if all_component_types[component_type_name][key] == int(component_type_numerical_value, 0):
+ del all_component_types[component_type_name][key]
+
+ all_component_types[component_type_name][component_type_literal] = int(component_type_numerical_value, 0)
+
+ logging.debug("type:{}, literal:{}, values:{}.".format(component_type_name, component_type_literal, component_type_numerical_value))
+
+ # Transform input source in inclusive criterion
+ shift = len(all_component_types['OutputDevicesMask'])
+ if shift > 32:
+ logging.critical("OutputDevicesMask incompatible with criterion representation on 32 bits")
+ logging.info("EXIT ON FAILURE")
+ exit(1)
+
+ for component_types in all_component_types:
+ values = ','.join('{}:{}'.format(value, key) for key, value in all_component_types[component_types].items())
+ logging.info("{}: <{}>".format(component_types, values))
+
+ return all_component_types
+
+
+def main():
+ logging.root.setLevel(logging.INFO)
+ args = parseArgs()
+ route_criteria = 0
+
+ all_component_types = parseAndroidAudioFile(args.androidaudiobaseheader)
+
+ generateXmlStructureFile(all_component_types, args.commontypesstructure, args.outputfile)
+
+# If this file is directly executed
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/services/audiopolicy/engineconfigurable/wrapper/Android.bp b/services/audiopolicy/engineconfigurable/wrapper/Android.bp
index 6f59487..301ecc0 100644
--- a/services/audiopolicy/engineconfigurable/wrapper/Android.bp
+++ b/services/audiopolicy/engineconfigurable/wrapper/Android.bp
@@ -11,7 +11,6 @@
"libbase_headers",
"libaudiopolicycommon",
],
- static_libs: ["libaudiopolicycomponents"],
shared_libs: [
"liblog",
"libutils",
diff --git a/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp b/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp
index 465a6f9..63990ac 100644
--- a/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp
+++ b/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp
@@ -92,7 +92,8 @@
template <>
struct ParameterManagerWrapper::parameterManagerElementSupported<ISelectionCriterionTypeInterface> {};
-ParameterManagerWrapper::ParameterManagerWrapper()
+ParameterManagerWrapper::ParameterManagerWrapper(bool enableSchemaVerification,
+ const std::string &schemaUri)
: mPfwConnectorLogger(new ParameterMgrPlatformConnectorLogger)
{
// Connector
@@ -104,6 +105,15 @@
// Logger
mPfwConnector->setLogger(mPfwConnectorLogger);
+
+ // Schema validation
+ std::string error;
+ bool ret = mPfwConnector->setValidateSchemasOnStart(enableSchemaVerification, error);
+ ALOGE_IF(!ret, "Failed to activate schema validation: %s", error.c_str());
+ if (enableSchemaVerification && ret && !schemaUri.empty()) {
+ ALOGE("Schema verification activated with schema URI: %s", schemaUri.c_str());
+ mPfwConnector->setSchemaUri(schemaUri);
+ }
}
status_t ParameterManagerWrapper::addCriterion(const std::string &name, bool isInclusive,
@@ -145,11 +155,10 @@
delete mPfwConnector;
}
-status_t ParameterManagerWrapper::start()
+status_t ParameterManagerWrapper::start(std::string &error)
{
ALOGD("%s: in", __FUNCTION__);
/// Start PFW
- std::string error;
if (!mPfwConnector->start(error)) {
ALOGE("%s: Policy PFW start error: %s", __FUNCTION__, error.c_str());
return NO_INIT;
@@ -253,13 +262,13 @@
return interface->getLiteralValue(valueToCheck, literalValue);
}
-status_t ParameterManagerWrapper::setDeviceConnectionState(const sp<DeviceDescriptor> devDesc,
- audio_policy_dev_state_t state)
+status_t ParameterManagerWrapper::setDeviceConnectionState(
+ audio_devices_t type, const std::string address, audio_policy_dev_state_t state)
{
- std::string criterionName = audio_is_output_device(devDesc->type()) ?
+ std::string criterionName = audio_is_output_device(type) ?
gOutputDeviceAddressCriterionName : gInputDeviceAddressCriterionName;
- ALOGV("%s: device with address %s %s", __FUNCTION__, devDesc->address().c_str(),
+ ALOGV("%s: device with address %s %s", __FUNCTION__, address.c_str(),
state != AUDIO_POLICY_DEVICE_STATE_AVAILABLE? "disconnected" : "connected");
ISelectionCriterionInterface *criterion =
getElement<ISelectionCriterionInterface>(criterionName, mPolicyCriteria);
@@ -271,8 +280,9 @@
auto criterionType = criterion->getCriterionType();
int deviceAddressId;
- if (not criterionType->getNumericalValue(devDesc->address().c_str(), deviceAddressId)) {
- ALOGW("%s: unknown device address reported (%s)", __FUNCTION__, devDesc->address().c_str());
+ if (not criterionType->getNumericalValue(address.c_str(), deviceAddressId)) {
+ ALOGW("%s: unknown device address reported (%s) for criterion %s", __FUNCTION__,
+ address.c_str(), criterionName.c_str());
return BAD_TYPE;
}
int currentValueMask = criterion->getCriterionState();
diff --git a/services/audiopolicy/engineconfigurable/wrapper/include/ParameterManagerWrapper.h b/services/audiopolicy/engineconfigurable/wrapper/include/ParameterManagerWrapper.h
index 8443008..62b129a 100644
--- a/services/audiopolicy/engineconfigurable/wrapper/include/ParameterManagerWrapper.h
+++ b/services/audiopolicy/engineconfigurable/wrapper/include/ParameterManagerWrapper.h
@@ -16,9 +16,6 @@
#pragma once
-#include <PolicyAudioPort.h>
-#include <HwModule.h>
-#include <DeviceDescriptor.h>
#include <system/audio.h>
#include <system/audio_policy.h>
#include <utils/Errors.h>
@@ -47,16 +44,18 @@
using Criteria = std::map<std::string, ISelectionCriterionInterface *>;
public:
- ParameterManagerWrapper();
+ ParameterManagerWrapper(bool enableSchemaVerification = false,
+ const std::string &schemaUri = {});
~ParameterManagerWrapper();
/**
* Starts the platform state service.
* It starts the parameter framework policy instance.
+ * @param[out] contains human readable error if starts failed
*
- * @return NO_ERROR if success, error code otherwise.
+ * @return NO_ERROR if success, error code otherwise, and error is set to human readable string.
*/
- status_t start();
+ status_t start(std::string &error);
/**
* The following API wrap policy action to criteria
@@ -117,7 +116,15 @@
*/
status_t setAvailableOutputDevices(audio_devices_t outputDevices);
- status_t setDeviceConnectionState(const sp<DeviceDescriptor> devDesc,
+ /**
+ * @brief setDeviceConnectionState propagates a state event on a given device(s)
+ * @param type bit mask of the device whose state has changed
+ * @param address of the device whose state has changed
+ * @param state new state of the given device
+ * @return NO_ERROR if new state corretly propagated to Engine Parameter-Framework, error
+ * code otherwise.
+ */
+ status_t setDeviceConnectionState(audio_devices_t type, const std::string address,
audio_policy_dev_state_t state);
/**
diff --git a/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_product_strategies.xml b/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_product_strategies.xml
index 9398743..a7388da 100644
--- a/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_product_strategies.xml
+++ b/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_product_strategies.xml
@@ -72,6 +72,12 @@
<Attributes> <Usage value="AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE"/> </Attributes>
<Attributes></Attributes>
</AttributesGroup>
+ <AttributesGroup streamType="AUDIO_STREAM_ASSISTANT" volumeGroup="assistant">
+ <Attributes>
+ <ContentType value="AUDIO_CONTENT_TYPE_SPEECH"/>
+ <Usage value="AUDIO_USAGE_ASSISTANT"/>
+ </Attributes>
+ </AttributesGroup>
<AttributesGroup streamType="AUDIO_STREAM_SYSTEM" volumeGroup="system">
<Attributes> <Usage value="AUDIO_USAGE_ASSISTANCE_SONIFICATION"/> </Attributes>
</AttributesGroup>
@@ -91,20 +97,5 @@
</AttributesGroup>
</ProductStrategy>
- <!-- Routing Strategy rerouting may be removed as following media??? -->
- <ProductStrategy name="STRATEGY_REROUTING">
- <AttributesGroup streamType="AUDIO_STREAM_REROUTING" volumeGroup="rerouting">
- <Attributes></Attributes>
- </AttributesGroup>
- </ProductStrategy>
-
- <!-- Default product strategy has empty attributes -->
- <ProductStrategy name="STRATEGY_PATCH">
- <AttributesGroup streamType="AUDIO_STREAM_PATCH" volumeGroup="patch">
- <Attributes></Attributes>
- </AttributesGroup>
- </ProductStrategy>
-
-
</ProductStrategies>
diff --git a/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_stream_volumes.xml b/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_stream_volumes.xml
index 707a184..d5c3896 100644
--- a/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_stream_volumes.xml
+++ b/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_stream_volumes.xml
@@ -207,25 +207,15 @@
</volumeGroup>
<volumeGroup>
- <name>rerouting</name>
+ <name>assistant</name>
<indexMin>0</indexMin>
- <indexMax>1</indexMax>
- <volume deviceCategory="DEVICE_CATEGORY_HEADSET" ref="FULL_SCALE_VOLUME_CURVE"/>
- <volume deviceCategory="DEVICE_CATEGORY_SPEAKER" ref="FULL_SCALE_VOLUME_CURVE"/>
- <volume deviceCategory="DEVICE_CATEGORY_EARPIECE" ref="FULL_SCALE_VOLUME_CURVE"/>
- <volume deviceCategory="DEVICE_CATEGORY_EXT_MEDIA" ref="FULL_SCALE_VOLUME_CURVE"/>
- <volume deviceCategory="DEVICE_CATEGORY_HEARING_AID" ref="FULL_SCALE_VOLUME_CURVE"/>
+ <indexMax>15</indexMax>
+ <volume deviceCategory="DEVICE_CATEGORY_HEADSET" ref="DEFAULT_MEDIA_VOLUME_CURVE"/>
+ <volume deviceCategory="DEVICE_CATEGORY_SPEAKER" ref="DEFAULT_DEVICE_CATEGORY_SPEAKER_VOLUME_CURVE"/>
+ <volume deviceCategory="DEVICE_CATEGORY_EARPIECE" ref="DEFAULT_MEDIA_VOLUME_CURVE"/>
+ <volume deviceCategory="DEVICE_CATEGORY_EXT_MEDIA" ref="DEFAULT_MEDIA_VOLUME_CURVE"/>
+ <volume deviceCategory="DEVICE_CATEGORY_HEARING_AID" ref="DEFAULT_HEARING_AID_VOLUME_CURVE"/>
</volumeGroup>
- <volumeGroup>
- <name>patch</name>
- <indexMin>0</indexMin>
- <indexMax>1</indexMax>
- <volume deviceCategory="DEVICE_CATEGORY_HEADSET" ref="FULL_SCALE_VOLUME_CURVE"/>
- <volume deviceCategory="DEVICE_CATEGORY_SPEAKER" ref="FULL_SCALE_VOLUME_CURVE"/>
- <volume deviceCategory="DEVICE_CATEGORY_EARPIECE" ref="FULL_SCALE_VOLUME_CURVE"/>
- <volume deviceCategory="DEVICE_CATEGORY_EXT_MEDIA" ref="FULL_SCALE_VOLUME_CURVE"/>
- <volume deviceCategory="DEVICE_CATEGORY_HEARING_AID" ref="FULL_SCALE_VOLUME_CURVE"/>
- </volumeGroup>
</volumeGroups>
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index a3fa974..a7d7cf6 100755
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -463,8 +463,8 @@
"getDevicesForStrategy() no default device defined");
}
- ALOGVV("getDevices"
- "ForStrategy() strategy %d, device %x", strategy, devices.types());
+ ALOGVV("getDevices ForStrategy() strategy %d, device %s",
+ strategy, dumpDeviceTypes(devices.types()).c_str());
return devices;
}
@@ -636,8 +636,9 @@
String8(preferredStrategyDevice.mAddress.c_str()),
AUDIO_FORMAT_DEFAULT);
if (preferredAvailableDevDescr != nullptr) {
- ALOGVV("%s using pref device 0x%08x/%s for strategy %u", __FUNCTION__,
- preferredStrategyDevice.mType, preferredStrategyDevice.mAddress, strategy);
+ ALOGVV("%s using pref device 0x%08x/%s for strategy %u",
+ __func__, preferredStrategyDevice.mType,
+ preferredStrategyDevice.mAddress.c_str(), strategy);
return DeviceVector(preferredAvailableDevDescr);
}
}
diff --git a/services/audiopolicy/managerdefault/Android.bp b/services/audiopolicy/managerdefault/Android.bp
index 1fa0d19..577b42f 100644
--- a/services/audiopolicy/managerdefault/Android.bp
+++ b/services/audiopolicy/managerdefault/Android.bp
@@ -15,7 +15,6 @@
"libutils",
"liblog",
"libaudiopolicy",
- "libsoundtrigger",
"libmedia_helper",
"libmediametrics",
"libbinder",
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 9d80a16..6ce3388 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -46,7 +46,6 @@
#include <utils/Log.h>
#include <media/AudioParameter.h>
#include <private/android_filesystem_config.h>
-#include <soundtrigger/SoundTrigger.h>
#include <system/audio.h>
#include "AudioPolicyManager.h"
#include <Serializer.h>
@@ -526,12 +525,12 @@
// release existing RX patch if any
if (mCallRxPatch != 0) {
- mpClientInterface->releaseAudioPatch(mCallRxPatch->mAfPatchHandle, 0);
+ releaseAudioPatchInternal(mCallRxPatch->getHandle());
mCallRxPatch.clear();
}
// release TX patch if any
if (mCallTxPatch != 0) {
- mpClientInterface->releaseAudioPatch(mCallTxPatch->mAfPatchHandle, 0);
+ releaseAudioPatchInternal(mCallTxPatch->getHandle());
mCallTxPatch.clear();
}
@@ -557,11 +556,9 @@
ALOGE("updateCallRouting() no telephony Tx and/or RX device");
return muteWaitMs;
}
- // do not create a patch (aka Sw Bridging) if Primary HW module has declared supporting a
- // route between telephony RX to Sink device and Source device to telephony TX
- const auto &primaryModule = telephonyRxModule;
- createRxPatch = !primaryModule->supportsPatch(rxSourceDevice, rxDevices.itemAt(0));
- createTxPatch = !primaryModule->supportsPatch(txSourceDevice, txSinkDevice);
+ // createAudioPatchInternal now supports both HW / SW bridging
+ createRxPatch = true;
+ createTxPatch = true;
} else {
// If the RX device is on the primary HW module, then use legacy routing method for
// voice calls via setOutputDevice() on primary output.
@@ -585,6 +582,15 @@
// assuming the device uses audio HAL V5.0 and above
}
if (createTxPatch) { // create TX path audio patch
+ // terminate active capture if on the same HW module as the call TX source device
+ // FIXME: would be better to refine to only inputs whose profile connects to the
+ // call TX device but this information is not in the audio patch and logic here must be
+ // symmetric to the one in startInput()
+ for (const auto& activeDesc : mInputs.getActiveInputs()) {
+ if (activeDesc->hasSameHwModuleAs(txSourceDevice)) {
+ closeActiveClients(activeDesc);
+ }
+ }
mCallTxPatch = createTelephonyPatch(false /*isRx*/, txSourceDevice, delayMs);
}
@@ -598,6 +604,8 @@
if (device == nullptr) {
return nullptr;
}
+
+ // @TODO: still ignoring the address, or not dealing platform with multiple telephony devices
if (isRx) {
patchBuilder.addSink(device).
addSource(mAvailableInputDevices.getDevice(
@@ -608,45 +616,15 @@
AUDIO_DEVICE_OUT_TELEPHONY_TX, String8(), AUDIO_FORMAT_DEFAULT));
}
- // @TODO: still ignoring the address, or not dealing platform with mutliple telephonydevices
- const sp<DeviceDescriptor> outputDevice = isRx ?
- device : mAvailableOutputDevices.getDevice(
- AUDIO_DEVICE_OUT_TELEPHONY_TX, String8(), AUDIO_FORMAT_DEFAULT);
- SortedVector<audio_io_handle_t> outputs =
- getOutputsForDevices(DeviceVector(outputDevice), mOutputs);
- const audio_io_handle_t output = selectOutput(outputs);
- // request to reuse existing output stream if one is already opened to reach the target device
- if (output != AUDIO_IO_HANDLE_NONE) {
- sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
- ALOG_ASSERT(!outputDesc->isDuplicated(), "%s() %s device output %d is duplicated", __func__,
- outputDevice->toString().c_str(), output);
- patchBuilder.addSource(outputDesc, { .stream = AUDIO_STREAM_PATCH });
+ audio_patch_handle_t patchHandle = AUDIO_PATCH_HANDLE_NONE;
+ status_t status =
+ createAudioPatchInternal(patchBuilder.patch(), &patchHandle, mUidCached, delayMs);
+ ssize_t index = mAudioPatches.indexOfKey(patchHandle);
+ if (status != NO_ERROR || index < 0) {
+ ALOGW("%s() error %d creating %s audio patch", __func__, status, isRx ? "RX" : "TX");
+ return nullptr;
}
-
- if (!isRx) {
- // terminate active capture if on the same HW module as the call TX source device
- // FIXME: would be better to refine to only inputs whose profile connects to the
- // call TX device but this information is not in the audio patch and logic here must be
- // symmetric to the one in startInput()
- for (const auto& activeDesc : mInputs.getActiveInputs()) {
- if (activeDesc->hasSameHwModuleAs(device)) {
- closeActiveClients(activeDesc);
- }
- }
- }
-
- audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE;
- status_t status = mpClientInterface->createAudioPatch(
- patchBuilder.patch(), &afPatchHandle, delayMs);
- ALOGW_IF(status != NO_ERROR,
- "%s() error %d creating %s audio patch", __func__, status, isRx ? "RX" : "TX");
- sp<AudioPatch> audioPatch;
- if (status == NO_ERROR) {
- audioPatch = new AudioPatch(patchBuilder.patch(), mUidCached);
- audioPatch->mAfPatchHandle = afPatchHandle;
- audioPatch->mUid = mUidCached;
- }
- return audioPatch;
+ return mAudioPatches.valueAt(index);
}
bool AudioPolicyManager::isDeviceOfModule(
@@ -729,11 +707,11 @@
updateCallRouting(rxDevices, delayMs);
} else if (oldState == AUDIO_MODE_IN_CALL) {
if (mCallRxPatch != 0) {
- mpClientInterface->releaseAudioPatch(mCallRxPatch->mAfPatchHandle, 0);
+ releaseAudioPatchInternal(mCallRxPatch->getHandle());
mCallRxPatch.clear();
}
if (mCallTxPatch != 0) {
- mpClientInterface->releaseAudioPatch(mCallTxPatch->mAfPatchHandle, 0);
+ releaseAudioPatchInternal(mCallTxPatch->getHandle());
mCallTxPatch.clear();
}
setOutputDevices(mPrimaryOutput, rxDevices, force, 0);
@@ -972,19 +950,21 @@
if (usePrimaryOutputFromPolicyMixes) {
*output = policyDesc->mIoHandle;
sp<AudioPolicyMix> mix = policyDesc->mPolicyMix.promote();
- sp<DeviceDescriptor> deviceDesc =
- mAvailableOutputDevices.getDevice(mix->mDeviceType,
- mix->mDeviceAddress,
- AUDIO_FORMAT_DEFAULT);
- *selectedDeviceId = deviceDesc != 0 ? deviceDesc->getId() : AUDIO_PORT_HANDLE_NONE;
+ if (mix != nullptr) {
+ sp<DeviceDescriptor> deviceDesc =
+ mAvailableOutputDevices.getDevice(mix->mDeviceType,
+ mix->mDeviceAddress,
+ AUDIO_FORMAT_DEFAULT);
+ *selectedDeviceId = deviceDesc != 0 ? deviceDesc->getId() : AUDIO_PORT_HANDLE_NONE;
- ALOGV("getOutputForAttr() returns output %d", *output);
- if (resultAttr->usage == AUDIO_USAGE_VIRTUAL_SOURCE) {
- *outputType = API_OUT_MIX_PLAYBACK;
- } else {
- *outputType = API_OUTPUT_LEGACY;
+ ALOGV("getOutputForAttr() returns output %d", *output);
+ if (resultAttr->usage == AUDIO_USAGE_VIRTUAL_SOURCE) {
+ *outputType = API_OUT_MIX_PLAYBACK;
+ } else {
+ *outputType = API_OUTPUT_LEGACY;
+ }
+ return NO_ERROR;
}
- return NO_ERROR;
}
// Virtual sources must always be dynamicaly or explicitly routed
if (resultAttr->usage == AUDIO_USAGE_VIRTUAL_SOURCE) {
@@ -1224,7 +1204,7 @@
devices.containsDeviceWithType(sink->ext.device.type) &&
(address.isEmpty() || strncmp(sink->ext.device.address, address.string(),
AUDIO_DEVICE_MAX_ADDRESS_LEN) == 0)) {
- releaseAudioPatch(patch->mHandle, mUidCached);
+ releaseAudioPatch(patch->getHandle(), mUidCached);
break;
}
}
@@ -1311,7 +1291,7 @@
const struct audio_port_config *source = &patch->mPatch.sources[j];
if (source->type == AUDIO_PORT_TYPE_DEVICE &&
source->ext.device.hw_module == msdModule->getHandle()) {
- msdPatches.addAudioPatch(patch->mHandle, patch);
+ msdPatches.addAudioPatch(patch->getHandle(), patch);
}
}
}
@@ -1370,6 +1350,14 @@
// For encoded streams force direct flag to prevent downstream mixing.
sinkConfig->flags.output = static_cast<audio_output_flags_t>(
sinkConfig->flags.output | AUDIO_OUTPUT_FLAG_DIRECT);
+ if (audio_is_iec61937_compatible(sinkConfig->format)) {
+ // For formats compatible with IEC61937 encapsulation, assume that
+ // the record thread input from MSD is IEC61937 framed (for proportional buffer sizing).
+ // Add the AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO flag so downstream HAL can distinguish between
+ // raw and IEC61937 framed streams.
+ sinkConfig->flags.output = static_cast<audio_output_flags_t>(
+ sinkConfig->flags.output | AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO);
+ }
sourceConfig->sample_rate = bestSinkConfig.sample_rate;
// Specify exact channel mask to prevent guessing by bit count in PatchPanel.
sourceConfig->channel_mask = audio_channel_mask_out_to_in(bestSinkConfig.channel_mask);
@@ -1433,7 +1421,7 @@
if (audio_patches_are_equal(¤tPatch->mPatch, patch)) {
return NO_ERROR;
}
- releaseAudioPatch(currentPatch->mHandle, mUidCached);
+ releaseAudioPatch(currentPatch->getHandle(), mUidCached);
}
status_t status = installPatch(__func__, -1 /*index*/, nullptr /*patchHandle*/,
patch, 0 /*delayMs*/, mUidCached, nullptr /*patchDescPtr*/);
@@ -1639,7 +1627,7 @@
DeviceVector devices;
sp<AudioPolicyMix> policyMix = outputDesc->mPolicyMix.promote();
const char *address = NULL;
- if (policyMix != NULL) {
+ if (policyMix != nullptr) {
audio_devices_t newDeviceType;
address = policyMix->mDeviceAddress.string();
if ((policyMix->mRouteFlags & MIX_ROUTE_FLAG_LOOP_BACK) == MIX_ROUTE_FLAG_LOOP_BACK) {
@@ -1821,7 +1809,7 @@
sp<AudioPolicyMix> policyMix = outputDesc->mPolicyMix.promote();
if (isSingleDeviceType(
outputDesc->devices().types(), &audio_is_remote_submix_device) &&
- policyMix != NULL &&
+ policyMix != nullptr &&
policyMix->mMixType == MIX_TYPE_RECORDERS) {
setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
@@ -2268,7 +2256,7 @@
if (status == NO_ERROR && inputDesc->activeCount() == 1) {
sp<AudioPolicyMix> policyMix = inputDesc->mPolicyMix.promote();
// if input maps to a dynamic policy with an activity listener, notify of state change
- if ((policyMix != NULL)
+ if ((policyMix != nullptr)
&& ((policyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) {
mpClientInterface->onDynamicPolicyMixStateUpdate(policyMix->mDeviceAddress,
MIX_STATE_MIXING);
@@ -2277,7 +2265,7 @@
DeviceVector primaryInputDevices = availablePrimaryModuleInputDevices();
if (primaryInputDevices.contains(device) &&
mInputs.activeInputsCountOnDevices(primaryInputDevices) == 1) {
- SoundTrigger::setCaptureState(true);
+ mpClientInterface->setSoundTriggerCaptureState(true);
}
// automatically enable the remote submix output when input is started if not
@@ -2285,7 +2273,7 @@
// For remote submix (a virtual device), we open only one input per capture request.
if (audio_is_remote_submix_device(inputDesc->getDeviceType())) {
String8 address = String8("");
- if (policyMix == NULL) {
+ if (policyMix == nullptr) {
address = String8("0");
} else if (policyMix->mMixType == MIX_TYPE_PLAYERS) {
address = policyMix->mDeviceAddress;
@@ -2332,7 +2320,7 @@
} else {
sp<AudioPolicyMix> policyMix = inputDesc->mPolicyMix.promote();
// if input maps to a dynamic policy with an activity listener, notify of state change
- if ((policyMix != NULL)
+ if ((policyMix != nullptr)
&& ((policyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) {
mpClientInterface->onDynamicPolicyMixStateUpdate(policyMix->mDeviceAddress,
MIX_STATE_IDLE);
@@ -2342,7 +2330,7 @@
// used by a policy mix of type MIX_TYPE_RECORDERS
if (audio_is_remote_submix_device(inputDesc->getDeviceType())) {
String8 address = String8("");
- if (policyMix == NULL) {
+ if (policyMix == nullptr) {
address = String8("0");
} else if (policyMix->mMixType == MIX_TYPE_PLAYERS) {
address = policyMix->mDeviceAddress;
@@ -2360,7 +2348,7 @@
DeviceVector primaryInputDevices = availablePrimaryModuleInputDevices();
if (primaryInputDevices.contains(inputDesc->getDevice()) &&
mInputs.activeInputsCountOnDevices(primaryInputDevices) == 0) {
- SoundTrigger::setCaptureState(false);
+ mpClientInterface->setSoundTriggerCaptureState(false);
}
inputDesc->clearPreemptedSessions();
}
@@ -2763,12 +2751,14 @@
int session,
int id)
{
- ssize_t index = mOutputs.indexOfKey(io);
- if (index < 0) {
- index = mInputs.indexOfKey(io);
+ if (session != AUDIO_SESSION_DEVICE) {
+ ssize_t index = mOutputs.indexOfKey(io);
if (index < 0) {
- ALOGW("registerEffect() unknown io %d", io);
- return INVALID_OPERATION;
+ index = mInputs.indexOfKey(io);
+ if (index < 0) {
+ ALOGW("registerEffect() unknown io %d", io);
+ return INVALID_OPERATION;
+ }
}
}
return mEffects.registerEffect(desc, io, session, id,
@@ -2788,16 +2778,6 @@
return mEffects.unregisterEffect(id);
}
-void AudioPolicyManager::cleanUpEffectsForIo(audio_io_handle_t io)
-{
- EffectDescriptorCollection effects = mEffects.getEffectsForIo(io);
- for (size_t i = 0; i < effects.size(); i++) {
- ALOGW("%s removing stale effect %s, id %d on closed IO %d",
- __func__, effects.valueAt(i)->mDesc.name, effects.keyAt(i), io);
- unregisterEffect(effects.keyAt(i));
- }
-}
-
status_t AudioPolicyManager::setEffectEnabled(int id, bool enabled)
{
sp<EffectDescriptor> effect = mEffects.getEffect(id);
@@ -3149,6 +3129,49 @@
return mEngine->getPreferredDeviceForStrategy(strategy, device);
}
+status_t AudioPolicyManager::setUserIdDeviceAffinities(int userId,
+ const Vector<AudioDeviceTypeAddr>& devices) {
+ ALOGI("%s() userId=%d num devices %zu", __FUNCTION__, userId, devices.size());
+ // userId/device affinity is only for output devices
+ for (size_t i = 0; i < devices.size(); i++) {
+ if (!audio_is_output_device(devices[i].mType)) {
+ ALOGE("%s() device=%08x is NOT an output device",
+ __FUNCTION__,
+ devices[i].mType);
+ return BAD_VALUE;
+ }
+ }
+
+ status_t status = mPolicyMixes.setUserIdDeviceAffinities(userId, devices);
+ if (status != NO_ERROR) {
+ ALOGE("%s() could not set device affinity for userId %d",
+ __FUNCTION__, userId);
+ return status;
+ }
+
+ // reevaluate outputs for all devices
+ checkForDeviceAndOutputChanges();
+ updateCallAndOutputRouting();
+
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManager::removeUserIdDeviceAffinities(int userId) {
+ ALOGI("%s() userId=%d", __FUNCTION__, userId);
+ status_t status = mPolicyMixes.removeUserIdDeviceAffinities(userId);
+ if (status != NO_ERROR) {
+ ALOGE("%s() Could not remove all device affinities fo userId = %d",
+ __FUNCTION__, userId);
+ return status;
+ }
+
+ // reevaluate outputs for all devices
+ checkForDeviceAndOutputChanges();
+ updateCallAndOutputRouting();
+
+ return NO_ERROR;
+}
+
void AudioPolicyManager::dump(String8 *dst) const
{
dst->appendFormat("\nAudioPolicyManager Dump: %p\n", this);
@@ -3396,16 +3419,16 @@
return BAD_VALUE;
}
-status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch,
- audio_patch_handle_t *handle,
- uid_t uid)
+status_t AudioPolicyManager::createAudioPatchInternal(const struct audio_patch *patch,
+ audio_patch_handle_t *handle,
+ uid_t uid, uint32_t delayMs,
+ const sp<SourceClientDescriptor>& sourceDesc)
{
- ALOGV("createAudioPatch()");
-
+ ALOGV("%s", __func__);
if (handle == NULL || patch == NULL) {
return BAD_VALUE;
}
- ALOGV("createAudioPatch() num sources %d num sinks %d", patch->num_sources, patch->num_sinks);
+ ALOGV("%s num sources %d num sinks %d", __func__, patch->num_sources, patch->num_sinks);
if (!audio_patch_is_valid(patch)) {
return BAD_VALUE;
@@ -3427,22 +3450,22 @@
sp<AudioPatch> patchDesc;
ssize_t index = mAudioPatches.indexOfKey(*handle);
- ALOGV("createAudioPatch source id %d role %d type %d", patch->sources[0].id,
- patch->sources[0].role,
- patch->sources[0].type);
+ ALOGV("%s source id %d role %d type %d", __func__, patch->sources[0].id,
+ patch->sources[0].role,
+ patch->sources[0].type);
#if LOG_NDEBUG == 0
for (size_t i = 0; i < patch->num_sinks; i++) {
- ALOGV("createAudioPatch sink %zu: id %d role %d type %d", i, patch->sinks[i].id,
- patch->sinks[i].role,
- patch->sinks[i].type);
+ ALOGV("%s sink %zu: id %d role %d type %d", __func__ ,i, patch->sinks[i].id,
+ patch->sinks[i].role,
+ patch->sinks[i].type);
}
#endif
if (index >= 0) {
patchDesc = mAudioPatches.valueAt(index);
- ALOGV("createAudioPatch() mUidCached %d patchDesc->mUid %d uid %d",
- mUidCached, patchDesc->mUid, uid);
- if (patchDesc->mUid != mUidCached && uid != patchDesc->mUid) {
+ ALOGV("%s mUidCached %d patchDesc->mUid %d uid %d",
+ __func__, mUidCached, patchDesc->getUid(), uid);
+ if (patchDesc->getUid() != mUidCached && uid != patchDesc->getUid()) {
return INVALID_OPERATION;
}
} else {
@@ -3452,15 +3475,15 @@
if (patch->sources[0].type == AUDIO_PORT_TYPE_MIX) {
sp<SwAudioOutputDescriptor> outputDesc = mOutputs.getOutputFromId(patch->sources[0].id);
if (outputDesc == NULL) {
- ALOGV("createAudioPatch() output not found for id %d", patch->sources[0].id);
+ ALOGV("%s output not found for id %d", __func__, patch->sources[0].id);
return BAD_VALUE;
}
ALOG_ASSERT(!outputDesc->isDuplicated(),"duplicated output %d in source in ports",
outputDesc->mIoHandle);
if (patchDesc != 0) {
if (patchDesc->mPatch.sources[0].id != patch->sources[0].id) {
- ALOGV("createAudioPatch() source id differs for patch current id %d new id %d",
- patchDesc->mPatch.sources[0].id, patch->sources[0].id);
+ ALOGV("%s source id differs for patch current id %d new id %d",
+ __func__, patchDesc->mPatch.sources[0].id, patch->sources[0].id);
return BAD_VALUE;
}
}
@@ -3469,13 +3492,13 @@
// Only support mix to devices connection
// TODO add support for mix to mix connection
if (patch->sinks[i].type != AUDIO_PORT_TYPE_DEVICE) {
- ALOGV("createAudioPatch() source mix but sink is not a device");
+ ALOGV("%s source mix but sink is not a device", __func__);
return INVALID_OPERATION;
}
sp<DeviceDescriptor> devDesc =
mAvailableOutputDevices.getDeviceFromId(patch->sinks[i].id);
if (devDesc == 0) {
- ALOGV("createAudioPatch() out device not found for id %d", patch->sinks[i].id);
+ ALOGV("%s out device not found for id %d", __func__, patch->sinks[i].id);
return BAD_VALUE;
}
@@ -3487,8 +3510,7 @@
patch->sources[0].channel_mask,
NULL, // updatedChannelMask
AUDIO_OUTPUT_FLAG_NONE /*FIXME*/)) {
- ALOGV("createAudioPatch() profile not supported for device %08x",
- devDesc->type());
+ ALOGV("%s profile not supported for device %08x", __func__, devDesc->type());
return INVALID_OPERATION;
}
devices.add(devDesc);
@@ -3498,19 +3520,19 @@
}
// TODO: reconfigure output format and channels here
- ALOGV("createAudioPatch() setting device %s on output %d",
- dumpDeviceTypes(devices.types()).c_str(), outputDesc->mIoHandle);
+ ALOGV("%s setting device %s on output %d",
+ __func__, dumpDeviceTypes(devices.types()).c_str(), outputDesc->mIoHandle);
setOutputDevices(outputDesc, devices, true, 0, handle);
index = mAudioPatches.indexOfKey(*handle);
if (index >= 0) {
if (patchDesc != 0 && patchDesc != mAudioPatches.valueAt(index)) {
- ALOGW("createAudioPatch() setOutputDevice() did not reuse the patch provided");
+ ALOGW("%s setOutputDevice() did not reuse the patch provided", __func__);
}
patchDesc = mAudioPatches.valueAt(index);
- patchDesc->mUid = uid;
- ALOGV("createAudioPatch() success");
+ patchDesc->setUid(uid);
+ ALOGV("%s success", __func__);
} else {
- ALOGW("createAudioPatch() setOutputDevice() failed to create a patch");
+ ALOGW("%s setOutputDevice() failed to create a patch", __func__);
return INVALID_OPERATION;
}
} else if (patch->sources[0].type == AUDIO_PORT_TYPE_DEVICE) {
@@ -3549,19 +3571,19 @@
return INVALID_OPERATION;
}
// TODO: reconfigure output format and channels here
- ALOGV("%s() setting device %s on output %d", __func__,
+ ALOGV("%s setting device %s on output %d", __func__,
device->toString().c_str(), inputDesc->mIoHandle);
setInputDevice(inputDesc->mIoHandle, device, true, handle);
index = mAudioPatches.indexOfKey(*handle);
if (index >= 0) {
if (patchDesc != 0 && patchDesc != mAudioPatches.valueAt(index)) {
- ALOGW("createAudioPatch() setInputDevice() did not reuse the patch provided");
+ ALOGW("%s setInputDevice() did not reuse the patch provided", __func__);
}
patchDesc = mAudioPatches.valueAt(index);
- patchDesc->mUid = uid;
- ALOGV("createAudioPatch() success");
+ patchDesc->setUid(uid);
+ ALOGV("%s success", __func__);
} else {
- ALOGW("createAudioPatch() setInputDevice() failed to create a patch");
+ ALOGW("%s setInputDevice() failed to create a patch", __func__);
return INVALID_OPERATION;
}
} else if (patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE) {
@@ -3579,55 +3601,97 @@
//update source and sink with our own data as the data passed in the patch may
// be incomplete.
- struct audio_patch newPatch = *patch;
- srcDevice->toAudioPortConfig(&newPatch.sources[0], &patch->sources[0]);
+ PatchBuilder patchBuilder;
+ audio_port_config sourcePortConfig = {};
+ srcDevice->toAudioPortConfig(&sourcePortConfig, &patch->sources[0]);
+ patchBuilder.addSource(sourcePortConfig);
for (size_t i = 0; i < patch->num_sinks; i++) {
if (patch->sinks[i].type != AUDIO_PORT_TYPE_DEVICE) {
- ALOGV("createAudioPatch() source device but one sink is not a device");
+ ALOGV("%s source device but one sink is not a device", __func__);
return INVALID_OPERATION;
}
-
sp<DeviceDescriptor> sinkDevice =
mAvailableOutputDevices.getDeviceFromId(patch->sinks[i].id);
if (sinkDevice == 0) {
return BAD_VALUE;
}
- sinkDevice->toAudioPortConfig(&newPatch.sinks[i], &patch->sinks[i]);
+ audio_port_config sinkPortConfig = {};
+ sinkDevice->toAudioPortConfig(&sinkPortConfig, &patch->sinks[i]);
+ patchBuilder.addSink(sinkPortConfig);
// create a software bridge in PatchPanel if:
// - source and sink devices are on different HW modules OR
// - audio HAL version is < 3.0
// - audio HAL version is >= 3.0 but no route has been declared between devices
+ // - called from startAudioSource (aka sourceDesc != nullptr) and source device does
+ // not have a gain controller
if (!srcDevice->hasSameHwModuleAs(sinkDevice) ||
(srcDevice->getModuleVersionMajor() < 3) ||
- !srcDevice->getModule()->supportsPatch(srcDevice, sinkDevice)) {
+ !srcDevice->getModule()->supportsPatch(srcDevice, sinkDevice) ||
+ (sourceDesc != nullptr &&
+ srcDevice->getAudioPort()->getGains().size() == 0)) {
// support only one sink device for now to simplify output selection logic
if (patch->num_sinks > 1) {
return INVALID_OPERATION;
}
- SortedVector<audio_io_handle_t> outputs =
- getOutputsForDevices(DeviceVector(sinkDevice), mOutputs);
- // if the sink device is reachable via an opened output stream, request to go via
- // this output stream by adding a second source to the patch description
- const audio_io_handle_t output = selectOutput(outputs);
- if (output != AUDIO_IO_HANDLE_NONE) {
- sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
- if (outputDesc->isDuplicated()) {
+ audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
+ if (sourceDesc != nullptr) {
+ // take care of dynamic routing for SwOutput selection,
+ audio_attributes_t attributes = sourceDesc->attributes();
+ audio_stream_type_t stream = sourceDesc->stream();
+ audio_attributes_t resultAttr;
+ audio_config_t config = AUDIO_CONFIG_INITIALIZER;
+ config.sample_rate = sourceDesc->config().sample_rate;
+ config.channel_mask = sourceDesc->config().channel_mask;
+ config.format = sourceDesc->config().format;
+ audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE;
+ audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
+ bool isRequestedDeviceForExclusiveUse = false;
+ std::vector<sp<SwAudioOutputDescriptor>> secondaryOutputs;
+ output_type_t outputType;
+ getOutputForAttrInt(&resultAttr, &output, AUDIO_SESSION_NONE, &attributes,
+ &stream, sourceDesc->uid(), &config, &flags,
+ &selectedDeviceId, &isRequestedDeviceForExclusiveUse,
+ &secondaryOutputs, &outputType);
+ if (output == AUDIO_IO_HANDLE_NONE) {
+ ALOGV("%s no output for device %s",
+ __FUNCTION__, sinkDevice->toString().c_str());
return INVALID_OPERATION;
}
- outputDesc->toAudioPortConfig(&newPatch.sources[1], &patch->sources[0]);
- newPatch.sources[1].ext.mix.usecase.stream = AUDIO_STREAM_PATCH;
- newPatch.num_sources = 2;
+ } else {
+ SortedVector<audio_io_handle_t> outputs =
+ getOutputsForDevices(DeviceVector(sinkDevice), mOutputs);
+ // if the sink device is reachable via an opened output stream, request to
+ // go via this output stream by adding a second source to the patch
+ // description
+ output = selectOutput(outputs);
+ }
+ if (output != AUDIO_IO_HANDLE_NONE) {
+ sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
+ if (outputDesc->isDuplicated()) {
+ ALOGV("%s output for device %s is duplicated",
+ __FUNCTION__, sinkDevice->toString().c_str());
+ return INVALID_OPERATION;
+ }
+ audio_port_config srcMixPortConfig = {};
+ outputDesc->toAudioPortConfig(&srcMixPortConfig, &patch->sources[0]);
+ if (sourceDesc != nullptr) {
+ sourceDesc->setSwOutput(outputDesc);
+ }
+ // for volume control, we may need a valid stream
+ srcMixPortConfig.ext.mix.usecase.stream = sourceDesc != nullptr ?
+ sourceDesc->stream() : AUDIO_STREAM_PATCH;
+ patchBuilder.addSource(srcMixPortConfig);
}
}
}
// TODO: check from routing capabilities in config file and other conflicting patches
- status_t status = installPatch(__func__, index, handle, &newPatch, 0, uid, &patchDesc);
+ status_t status = installPatch(
+ __func__, index, handle, patchBuilder.patch(), delayMs, uid, &patchDesc);
if (status != NO_ERROR) {
- ALOGW("createAudioPatch() patch panel could not connect device patch, error %d",
- status);
+ ALOGW("%s patch panel could not connect device patch, error %d", __func__, status);
return INVALID_OPERATION;
}
} else {
@@ -3650,18 +3714,29 @@
return BAD_VALUE;
}
sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
- ALOGV("releaseAudioPatch() mUidCached %d patchDesc->mUid %d uid %d",
- mUidCached, patchDesc->mUid, uid);
- if (patchDesc->mUid != mUidCached && uid != patchDesc->mUid) {
+ ALOGV("%s() mUidCached %d patchDesc->mUid %d uid %d",
+ __func__, mUidCached, patchDesc->getUid(), uid);
+ if (patchDesc->getUid() != mUidCached && uid != patchDesc->getUid()) {
return INVALID_OPERATION;
}
+ return releaseAudioPatchInternal(handle);
+}
+status_t AudioPolicyManager::releaseAudioPatchInternal(audio_patch_handle_t handle,
+ uint32_t delayMs)
+{
+ ALOGV("%s patch %d", __func__, handle);
+ if (mAudioPatches.indexOfKey(handle) < 0) {
+ ALOGE("%s: no patch found with handle=%d", __func__, handle);
+ return BAD_VALUE;
+ }
+ sp<AudioPatch> patchDesc = mAudioPatches.valueFor(handle);
struct audio_patch *patch = &patchDesc->mPatch;
- patchDesc->mUid = mUidCached;
+ patchDesc->setUid(mUidCached);
if (patch->sources[0].type == AUDIO_PORT_TYPE_MIX) {
sp<SwAudioOutputDescriptor> outputDesc = mOutputs.getOutputFromId(patch->sources[0].id);
if (outputDesc == NULL) {
- ALOGV("releaseAudioPatch() output not found for id %d", patch->sources[0].id);
+ ALOGV("%s output not found for id %d", __func__, patch->sources[0].id);
return BAD_VALUE;
}
@@ -3674,7 +3749,7 @@
if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
sp<AudioInputDescriptor> inputDesc = mInputs.getInputFromId(patch->sinks[0].id);
if (inputDesc == NULL) {
- ALOGV("releaseAudioPatch() input not found for id %d", patch->sinks[0].id);
+ ALOGV("%s input not found for id %d", __func__, patch->sinks[0].id);
return BAD_VALUE;
}
setInputDevice(inputDesc->mIoHandle,
@@ -3682,10 +3757,11 @@
true,
NULL);
} else if (patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE) {
- status_t status = mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
- ALOGV("releaseAudioPatch() patch panel returned %d patchHandle %d",
- status, patchDesc->mAfPatchHandle);
- removeAudioPatch(patchDesc->mHandle);
+ status_t status =
+ mpClientInterface->releaseAudioPatch(patchDesc->getAfHandle(), delayMs);
+ ALOGV("%s patch panel returned %d patchHandle %d",
+ __func__, status, patchDesc->getAfHandle());
+ removeAudioPatch(patchDesc->getHandle());
nextAudioPortGeneration();
mpClientInterface->onAudioPatchListUpdate();
} else {
@@ -3783,7 +3859,7 @@
{
for (ssize_t i = (ssize_t)mAudioPatches.size() - 1; i >= 0; i--) {
sp<AudioPatch> patchDesc = mAudioPatches.valueAt(i);
- if (patchDesc->mUid == uid) {
+ if (patchDesc->getUid() == uid) {
releaseAudioPatch(mAudioPatches.keyAt(i), uid);
}
}
@@ -3919,11 +3995,8 @@
*portId = PolicyAudioPort::getNextUniqueId();
- struct audio_patch dummyPatch = {};
- sp<AudioPatch> patchDesc = new AudioPatch(&dummyPatch, uid);
-
sp<SourceClientDescriptor> sourceDesc =
- new SourceClientDescriptor(*portId, uid, *attributes, patchDesc, srcDevice,
+ new SourceClientDescriptor(*portId, uid, *attributes, *source, srcDevice,
mEngine->getStreamTypeForAttributes(*attributes),
mEngine->getProductStrategyForAttributes(*attributes),
toVolumeSource(*attributes));
@@ -3943,7 +4016,6 @@
disconnectAudioSource(sourceDesc);
audio_attributes_t attributes = sourceDesc->attributes();
- audio_stream_type_t stream = sourceDesc->stream();
sp<DeviceDescriptor> srcDevice = sourceDesc->srcDevice();
DeviceVector sinkDevices =
@@ -3953,92 +4025,55 @@
ALOG_ASSERT(mAvailableOutputDevices.contains(sinkDevice), "%s: Device %s not available",
__FUNCTION__, sinkDevice->toString().c_str());
- audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE;
-
- if (srcDevice->hasSameHwModuleAs(sinkDevice) &&
- srcDevice->getModuleVersionMajor() >= 3 &&
- sinkDevice->getModule()->supportsPatch(srcDevice, sinkDevice) &&
- srcDevice->getAudioPort()->getGains().size() > 0) {
- ALOGV("%s Device to Device route supported by >=3.0 HAL", __FUNCTION__);
- // TODO: may explicitly specify whether we should use HW or SW patch
- // create patch between src device and output device
- // create Hwoutput and add to mHwOutputs
- } else {
- audio_attributes_t resultAttr;
- audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
- audio_config_t config = AUDIO_CONFIG_INITIALIZER;
- config.sample_rate = sourceDesc->config().sample_rate;
- config.channel_mask = sourceDesc->config().channel_mask;
- config.format = sourceDesc->config().format;
- audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE;
- audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
- bool isRequestedDeviceForExclusiveUse = false;
- std::vector<sp<SwAudioOutputDescriptor>> secondaryOutputs;
- output_type_t outputType;
- getOutputForAttrInt(&resultAttr, &output, AUDIO_SESSION_NONE,
- &attributes, &stream, sourceDesc->uid(), &config, &flags,
- &selectedDeviceId, &isRequestedDeviceForExclusiveUse,
- &secondaryOutputs, &outputType);
- if (output == AUDIO_IO_HANDLE_NONE) {
- ALOGV("%s no output for device %s",
- __FUNCTION__, dumpDeviceTypes(sinkDevices.types()).c_str());
- return INVALID_OPERATION;
- }
- sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
- if (outputDesc->isDuplicated()) {
- ALOGV("%s output for device %s is duplicated",
- __FUNCTION__, dumpDeviceTypes(sinkDevices.types()).c_str());
- return INVALID_OPERATION;
- }
- status_t status = outputDesc->start();
+ PatchBuilder patchBuilder;
+ patchBuilder.addSink(sinkDevice).addSource(srcDevice);
+ audio_patch_handle_t handle = AUDIO_PATCH_HANDLE_NONE;
+ status_t status =
+ createAudioPatchInternal(patchBuilder.patch(), &handle, mUidCached, 0, sourceDesc);
+ if (status != NO_ERROR || mAudioPatches.indexOfKey(handle) < 0) {
+ ALOGW("%s patch panel could not connect device patch, error %d", __func__, status);
+ return INVALID_OPERATION;
+ }
+ sourceDesc->setPatchHandle(handle);
+ // SW Bridge? (@todo: HW bridge, keep track of HwOutput for device selection "reconsideration")
+ sp<SwAudioOutputDescriptor> swOutput = sourceDesc->swOutput().promote();
+ if (swOutput != 0) {
+ status = swOutput->start();
if (status != NO_ERROR) {
- return status;
+ goto FailureSourceAdded;
}
-
- // create a special patch with no sink and two sources:
- // - the second source indicates to PatchPanel through which output mix this patch should
- // be connected as well as the stream type for volume control
- // - the sink is defined by whatever output device is currently selected for the output
- // though which this patch is routed.
- PatchBuilder patchBuilder;
- patchBuilder.addSource(srcDevice).addSource(outputDesc, { .stream = stream });
- status = mpClientInterface->createAudioPatch(patchBuilder.patch(),
- &afPatchHandle,
- 0);
- ALOGV("%s patch panel returned %d patchHandle %d", __FUNCTION__,
- status, afPatchHandle);
- sourceDesc->patchDesc()->mPatch = *patchBuilder.patch();
- if (status != NO_ERROR) {
- ALOGW("%s patch panel could not connect device patch, error %d",
- __FUNCTION__, status);
- return INVALID_OPERATION;
- }
-
- if (outputDesc->getClient(sourceDesc->portId()) != nullptr) {
+ if (swOutput->getClient(sourceDesc->portId()) != nullptr) {
ALOGW("%s source portId has already been attached to outputDesc", __func__);
- return INVALID_OPERATION;
+ goto FailureReleasePatch;
}
- outputDesc->addClient(sourceDesc);
-
+ swOutput->addClient(sourceDesc);
uint32_t delayMs = 0;
- status = startSource(outputDesc, sourceDesc, &delayMs);
-
+ status = startSource(swOutput, sourceDesc, &delayMs);
if (status != NO_ERROR) {
- mpClientInterface->releaseAudioPatch(sourceDesc->patchDesc()->mAfPatchHandle, 0);
- outputDesc->removeClient(sourceDesc->portId());
- outputDesc->stop();
- return status;
+ ALOGW("%s failed to start source, error %d", __FUNCTION__, status);
+ goto FailureSourceActive;
}
- sourceDesc->setSwOutput(outputDesc);
if (delayMs != 0) {
usleep(delayMs * 1000);
}
+ } else {
+ sp<HwAudioOutputDescriptor> hwOutputDesc = sourceDesc->hwOutput().promote();
+ if (hwOutputDesc != 0) {
+ // create Hwoutput and add to mHwOutputs
+ } else {
+ ALOGW("%s source has neither SW nor HW output", __FUNCTION__);
+ }
}
-
- sourceDesc->patchDesc()->mAfPatchHandle = afPatchHandle;
- addAudioPatch(sourceDesc->patchDesc()->mHandle, sourceDesc->patchDesc());
-
return NO_ERROR;
+
+FailureSourceActive:
+ swOutput->stop();
+ releaseOutput(sourceDesc->portId());
+FailureSourceAdded:
+ sourceDesc->setSwOutput(nullptr);
+FailureReleasePatch:
+ releaseAudioPatchInternal(handle);
+ return INVALID_OPERATION;
}
status_t AudioPolicyManager::stopAudioSource(audio_port_handle_t portId)
@@ -4282,33 +4317,22 @@
status_t AudioPolicyManager::disconnectAudioSource(const sp<SourceClientDescriptor>& sourceDesc)
{
ALOGV("%s port Id %d", __FUNCTION__, sourceDesc->portId());
-
- sp<AudioPatch> patchDesc = mAudioPatches.valueFor(sourceDesc->patchDesc()->mHandle);
- if (patchDesc == 0) {
- ALOGW("%s source has no patch with handle %d", __FUNCTION__,
- sourceDesc->patchDesc()->mHandle);
- return BAD_VALUE;
- }
- removeAudioPatch(sourceDesc->patchDesc()->mHandle);
-
- sp<SwAudioOutputDescriptor> swOutputDesc = sourceDesc->swOutput().promote();
- if (swOutputDesc != 0) {
- status_t status = stopSource(swOutputDesc, sourceDesc);
+ sp<SwAudioOutputDescriptor> swOutput = sourceDesc->swOutput().promote();
+ if (swOutput != 0) {
+ status_t status = stopSource(swOutput, sourceDesc);
if (status == NO_ERROR) {
- swOutputDesc->stop();
+ swOutput->stop();
}
- swOutputDesc->removeClient(sourceDesc->portId());
- mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
+ releaseOutput(sourceDesc->portId());
} else {
sp<HwAudioOutputDescriptor> hwOutputDesc = sourceDesc->hwOutput().promote();
if (hwOutputDesc != 0) {
- // release patch between src device and output device
// close Hwoutput and remove from mHwOutputs
} else {
ALOGW("%s source has neither SW nor HW output", __FUNCTION__);
}
}
- return NO_ERROR;
+ return releaseAudioPatchInternal(sourceDesc->getPatchHandle());
}
sp<SourceClientDescriptor> AudioPolicyManager::getSourceForAttributesOnOutput(
@@ -5016,7 +5040,8 @@
ssize_t index = mAudioPatches.indexOfKey(closingOutput->getPatchHandle());
if (index >= 0) {
sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
- (void) /*status_t status*/ mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
+ (void) /*status_t status*/ mpClientInterface->releaseAudioPatch(
+ patchDesc->getAfHandle(), 0);
mAudioPatches.removeItemsAt(index);
mpClientInterface->onAudioPatchListUpdate();
}
@@ -5044,8 +5069,6 @@
setMsdPatch();
}
}
-
- cleanUpEffectsForIo(output);
}
void AudioPolicyManager::closeInput(audio_io_handle_t input)
@@ -5064,7 +5087,8 @@
ssize_t index = mAudioPatches.indexOfKey(inputDesc->getPatchHandle());
if (index >= 0) {
sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
- (void) /*status_t status*/ mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
+ (void) /*status_t status*/ mpClientInterface->releaseAudioPatch(
+ patchDesc->getAfHandle(), 0);
mAudioPatches.removeItemsAt(index);
mpClientInterface->onAudioPatchListUpdate();
}
@@ -5075,10 +5099,8 @@
DeviceVector primaryInputDevices = availablePrimaryModuleInputDevices();
if (primaryInputDevices.contains(device) &&
mInputs.activeInputsCountOnDevices(primaryInputDevices) == 0) {
- SoundTrigger::setCaptureState(false);
+ mpClientInterface->setSoundTriggerCaptureState(false);
}
-
- cleanUpEffectsForIo(input);
}
SortedVector<audio_io_handle_t> AudioPolicyManager::getOutputsForDevices(
@@ -5317,7 +5339,7 @@
ssize_t index = mAudioPatches.indexOfKey(outputDesc->getPatchHandle());
if (index >= 0) {
sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
- if (patchDesc->mUid != mUidCached) {
+ if (patchDesc->getUid() != mUidCached) {
ALOGV("%s device %s forced by patch %d", __func__,
outputDesc->devices().toString().c_str(), outputDesc->getPatchHandle());
return outputDesc->devices();
@@ -5345,7 +5367,8 @@
auto attr = mEngine->getAllAttributesForProductStrategy(productStrategy).front();
if ((hasVoiceStream(streams) &&
- (isInCall() || mOutputs.isStrategyActiveOnSameModule(productStrategy, outputDesc))) ||
+ (isInCall() || mOutputs.isStrategyActiveOnSameModule(productStrategy, outputDesc)) &&
+ !isStreamActive(AUDIO_STREAM_ENFORCED_AUDIBLE, 0)) ||
((hasStream(streams, AUDIO_STREAM_ALARM) || hasStream(streams, AUDIO_STREAM_ENFORCED_AUDIBLE)) &&
mOutputs.isStrategyActiveOnSameModule(productStrategy, outputDesc)) ||
outputDesc->isStrategyActive(productStrategy)) {
@@ -5367,7 +5390,7 @@
ssize_t index = mAudioPatches.indexOfKey(inputDesc->getPatchHandle());
if (index >= 0) {
sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
- if (patchDesc->mUid != mUidCached) {
+ if (patchDesc->getUid() != mUidCached) {
ALOGV("getNewInputDevice() device %s forced by patch %d",
inputDesc->getDevice()->toString().c_str(), inputDesc->getPatchHandle());
return inputDesc->getDevice();
@@ -5441,6 +5464,35 @@
return deviceTypesToBitMask(devices.types());
}
+status_t AudioPolicyManager::getDevicesForAttributes(
+ const audio_attributes_t &attr, AudioDeviceTypeAddrVector *devices) {
+ if (devices == nullptr) {
+ return BAD_VALUE;
+ }
+ // check dynamic policies but only for primary descriptors (secondary not used for audible
+ // audio routing, only used for duplication for playback capture)
+ sp<SwAudioOutputDescriptor> policyDesc;
+ status_t status = mPolicyMixes.getOutputForAttr(attr, 0 /*uid unknown here*/,
+ AUDIO_OUTPUT_FLAG_NONE, policyDesc, nullptr);
+ if (status != OK) {
+ return status;
+ }
+ if (policyDesc != nullptr) {
+ sp<AudioPolicyMix> mix = policyDesc->mPolicyMix.promote();
+ if (mix != nullptr) {
+ AudioDeviceTypeAddr device(mix->mDeviceType, mix->mDeviceAddress.c_str());
+ devices->push_back(device);
+ return NO_ERROR;
+ }
+ }
+
+ DeviceVector curDevices = mEngine->getOutputDevicesForAttributes(attr, nullptr, false);
+ for (const auto& device : curDevices) {
+ devices->push_back(device->getDeviceTypeAddr());
+ }
+ return NO_ERROR;
+}
+
void AudioPolicyManager::handleNotificationRoutingForStream(audio_stream_type_t stream) {
switch(stream) {
case AUDIO_STREAM_MUSIC:
@@ -5694,10 +5746,10 @@
return INVALID_OPERATION;
}
sp< AudioPatch> patchDesc = mAudioPatches.valueAt(index);
- status_t status = mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, delayMs);
+ status_t status = mpClientInterface->releaseAudioPatch(patchDesc->getAfHandle(), delayMs);
ALOGV("resetOutputDevice() releaseAudioPatch returned %d", status);
outputDesc->setPatchHandle(AUDIO_PATCH_HANDLE_NONE);
- removeAudioPatch(patchDesc->mHandle);
+ removeAudioPatch(patchDesc->getHandle());
nextAudioPortGeneration();
mpClientInterface->onAudioPatchListUpdate();
return status;
@@ -5747,10 +5799,10 @@
return INVALID_OPERATION;
}
sp< AudioPatch> patchDesc = mAudioPatches.valueAt(index);
- status_t status = mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
+ status_t status = mpClientInterface->releaseAudioPatch(patchDesc->getAfHandle(), 0);
ALOGV("resetInputDevice() releaseAudioPatch returned %d", status);
inputDesc->setPatchHandle(AUDIO_PATCH_HANDLE_NONE);
- removeAudioPatch(patchDesc->mHandle);
+ removeAudioPatch(patchDesc->getHandle());
nextAudioPortGeneration();
mpClientInterface->onAudioPatchListUpdate();
return status;
@@ -6020,7 +6072,7 @@
int delayMs,
bool force)
{
- ALOGVV("applyStreamVolumes() for device %08x", device);
+ ALOGVV("applyStreamVolumes() for device %s", dumpDeviceTypes(deviceTypes).c_str());
for (const auto &volumeGroup : mEngine->getVolumeGroups()) {
auto &curves = getVolumeCurves(toVolumeSource(volumeGroup));
checkAndSetVolume(curves, toVolumeSource(volumeGroup),
@@ -6114,6 +6166,10 @@
case AUDIO_USAGE_VIRTUAL_SOURCE:
case AUDIO_USAGE_ASSISTANT:
case AUDIO_USAGE_CALL_ASSISTANT:
+ case AUDIO_USAGE_EMERGENCY:
+ case AUDIO_USAGE_SAFETY:
+ case AUDIO_USAGE_VEHICLE_STATUS:
+ case AUDIO_USAGE_ANNOUNCEMENT:
break;
default:
return false;
@@ -6172,8 +6228,8 @@
}
}
if (release) {
- ALOGV("%s releasing patch %u", __FUNCTION__, patchDesc->mHandle);
- releaseAudioPatch(patchDesc->mHandle, patchDesc->mUid);
+ ALOGV("%s releasing patch %u", __FUNCTION__, patchDesc->getHandle());
+ releaseAudioPatch(patchDesc->getHandle(), patchDesc->getUid());
}
}
@@ -6348,7 +6404,7 @@
status_t status = installPatch(
caller, index, patchHandle, patch, delayMs, mUidCached, &patchDesc);
if (status == NO_ERROR) {
- ioDescriptor->setPatchHandle(patchDesc->mHandle);
+ ioDescriptor->setPatchHandle(patchDesc->getHandle());
}
return status;
}
@@ -6365,7 +6421,7 @@
audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE;
if (index >= 0) {
patchDesc = mAudioPatches.valueAt(index);
- afPatchHandle = patchDesc->mAfPatchHandle;
+ afPatchHandle = patchDesc->getAfHandle();
}
status_t status = mpClientInterface->createAudioPatch(patch, &afPatchHandle, delayMs);
@@ -6374,13 +6430,13 @@
if (status == NO_ERROR) {
if (index < 0) {
patchDesc = new AudioPatch(patch, uid);
- addAudioPatch(patchDesc->mHandle, patchDesc);
+ addAudioPatch(patchDesc->getHandle(), patchDesc);
} else {
patchDesc->mPatch = *patch;
}
- patchDesc->mAfPatchHandle = afPatchHandle;
+ patchDesc->setAfHandle(afPatchHandle);
if (patchHandle) {
- *patchHandle = patchDesc->mHandle;
+ *patchHandle = patchDesc->getHandle();
}
nextAudioPortGeneration();
mpClientInterface->onAudioPatchListUpdate();
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index f6e4fc1..b0adbc7 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -193,6 +193,10 @@
// return the enabled output devices for the given stream type
virtual audio_devices_t getDevicesForStream(audio_stream_type_t stream);
+ virtual status_t getDevicesForAttributes(
+ const audio_attributes_t &attributes,
+ AudioDeviceTypeAddrVector *devices);
+
virtual audio_io_handle_t getOutputForEffect(const effect_descriptor_t *desc = NULL);
virtual status_t registerEffect(const effect_descriptor_t *desc,
audio_io_handle_t io,
@@ -234,7 +238,10 @@
virtual status_t getAudioPort(struct audio_port *port);
virtual status_t createAudioPatch(const struct audio_patch *patch,
audio_patch_handle_t *handle,
- uid_t uid);
+ uid_t uid) {
+ return createAudioPatchInternal(patch, handle, uid);
+ }
+
virtual status_t releaseAudioPatch(audio_patch_handle_t handle,
uid_t uid);
virtual status_t listAudioPatches(unsigned int *num_patches,
@@ -258,6 +265,9 @@
virtual status_t setUidDeviceAffinities(uid_t uid,
const Vector<AudioDeviceTypeAddr>& devices);
virtual status_t removeUidDeviceAffinities(uid_t uid);
+ virtual status_t setUserIdDeviceAffinities(int userId,
+ const Vector<AudioDeviceTypeAddr>& devices);
+ virtual status_t removeUserIdDeviceAffinities(int userId);
virtual status_t setPreferredDeviceForStrategy(product_strategy_t strategy,
const AudioDeviceTypeAddr &device);
@@ -874,6 +884,29 @@
param.addInt(String8(AudioParameter::keyMonoOutput), (int)mMasterMono);
mpClientInterface->setParameters(output, param.toString());
}
+
+ /**
+ * @brief createAudioPatchInternal internal function to manage audio patch creation
+ * @param[in] patch structure containing sink and source ports configuration
+ * @param[out] handle patch handle to be provided if patch installed correctly
+ * @param[in] uid of the client
+ * @param[in] delayMs if required
+ * @param[in] sourceDesc [optional] in case of external source, source client to be
+ * configured by the patch, i.e. assigning an Output (HW or SW)
+ * @return NO_ERROR if patch installed correctly, error code otherwise.
+ */
+ status_t createAudioPatchInternal(const struct audio_patch *patch,
+ audio_patch_handle_t *handle,
+ uid_t uid, uint32_t delayMs = 0,
+ const sp<SourceClientDescriptor>& sourceDesc = nullptr);
+ /**
+ * @brief releaseAudioPatchInternal internal function to remove an audio patch
+ * @param[in] handle of the patch to be removed
+ * @param[in] delayMs if required
+ * @return NO_ERROR if patch removed correctly, error code otherwise.
+ */
+ status_t releaseAudioPatchInternal(audio_patch_handle_t handle, uint32_t delayMs = 0);
+
status_t installPatch(const char *caller,
audio_patch_handle_t *patchHandle,
AudioIODescriptorInterface *ioDescriptor,
@@ -886,8 +919,6 @@
int delayMs,
uid_t uid,
sp<AudioPatch> *patchDescPtr);
-
- void cleanUpEffectsForIo(audio_io_handle_t io);
};
};
diff --git a/services/audiopolicy/service/Android.mk b/services/audiopolicy/service/Android.mk
index fdf3eae..d34dbe3 100644
--- a/services/audiopolicy/service/Android.mk
+++ b/services/audiopolicy/service/Android.mk
@@ -26,12 +26,14 @@
libaudioutils \
libaudiofoundation \
libhardware_legacy \
+ libaudiopolicy \
libaudiopolicymanager \
libmedia_helper \
libmediametrics \
libmediautils \
libeffectsconfig \
- libsensorprivacy
+ libsensorprivacy \
+ soundtrigger_middleware-aidl-cpp
LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := \
libsensorprivacy
diff --git a/services/audiopolicy/service/AudioPolicyClientImpl.cpp b/services/audiopolicy/service/AudioPolicyClientImpl.cpp
index 6de0c80..5b81b9d 100644
--- a/services/audiopolicy/service/AudioPolicyClientImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyClientImpl.cpp
@@ -17,10 +17,13 @@
#define LOG_TAG "AudioPolicyClientImpl"
//#define LOG_NDEBUG 0
-#include <soundtrigger/SoundTrigger.h>
-#include <utils/Log.h>
#include "AudioPolicyService.h"
+#include <android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.h>
+#include <utils/Log.h>
+
+#include "BinderProxy.h"
+
namespace android {
/* implementation of the client interface from the policy manager */
@@ -239,4 +242,12 @@
return AudioSystem::newAudioUniqueId(use);
}
+void AudioPolicyService::AudioPolicyClient::setSoundTriggerCaptureState(bool active) {
+ using media::soundtrigger_middleware::ISoundTriggerMiddlewareService;
+
+ static BinderProxy<ISoundTriggerMiddlewareService>
+ proxy("soundtrigger_middleware");
+ proxy.waitServiceOrDie()->setExternalCaptureState(active);
+}
+
} // namespace android
diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp
index 4947714..738a279 100644
--- a/services/audiopolicy/service/AudioPolicyEffects.cpp
+++ b/services/audiopolicy/service/AudioPolicyEffects.cpp
@@ -42,7 +42,10 @@
AudioPolicyEffects::AudioPolicyEffects()
{
status_t loadResult = loadAudioEffectXmlConfig();
- if (loadResult < 0) {
+ if (loadResult == NO_ERROR) {
+ mDefaultDeviceEffectFuture = std::async(
+ std::launch::async, &AudioPolicyEffects::initDefaultDeviceEffects, this);
+ } else if (loadResult < 0) {
ALOGW("Failed to load XML effect configuration, fallback to .conf");
// load automatic audio effect modules
if (access(AUDIO_EFFECT_VENDOR_CONFIG_FILE, R_OK) == 0) {
@@ -562,7 +565,8 @@
AUDIO_STREAM_BLUETOOTH_SCO_TAG,
AUDIO_STREAM_ENFORCED_AUDIBLE_TAG,
AUDIO_STREAM_DTMF_TAG,
- AUDIO_STREAM_TTS_TAG
+ AUDIO_STREAM_TTS_TAG,
+ AUDIO_STREAM_ASSISTANT_TAG
};
// returns the audio_stream_t enum corresponding to the output stream name or
@@ -907,8 +911,24 @@
streams.add(stream.type, effectDescs.release());
}
};
+
+ auto loadDeviceProcessingChain = [](auto &processingChain, auto& devicesEffects) {
+ for (auto& deviceProcess : processingChain) {
+
+ auto effectDescs = std::make_unique<EffectDescVector>();
+ for (auto& effect : deviceProcess.effects) {
+ effectDescs->mEffects.add(
+ new EffectDesc{effect.get().name.c_str(), effect.get().uuid});
+ }
+ auto deviceEffects = std::make_unique<DeviceEffects>(
+ std::move(effectDescs), deviceProcess.type, deviceProcess.address);
+ devicesEffects.emplace(deviceProcess.address, std::move(deviceEffects));
+ }
+ };
+
loadProcessingChain(result.parsedConfig->preprocess, mInputSources);
loadProcessingChain(result.parsedConfig->postprocess, mOutputStreams);
+ 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;
}
@@ -941,5 +961,32 @@
return NO_ERROR;
}
+void AudioPolicyEffects::initDefaultDeviceEffects()
+{
+ Mutex::Autolock _l(mLock);
+ for (const auto& deviceEffectsIter : mDeviceEffects) {
+ const auto& deviceEffects = deviceEffectsIter.second;
+ for (const auto& effectDesc : deviceEffects->mEffectDescriptors->mEffects) {
+ auto fx = std::make_unique<AudioEffect>(
+ EFFECT_UUID_NULL, String16("android"), &effectDesc->mUuid, 0, nullptr,
+ nullptr, AUDIO_SESSION_DEVICE, AUDIO_IO_HANDLE_NONE,
+ AudioDeviceTypeAddr{deviceEffects->getDeviceType(),
+ deviceEffects->getDeviceAddress()});
+ status_t status = fx->initCheck();
+ if (status != NO_ERROR && status != ALREADY_EXISTS) {
+ ALOGE("%s(): failed to create Fx %s on port type=%d address=%s", __func__,
+ effectDesc->mName, deviceEffects->getDeviceType(),
+ deviceEffects->getDeviceAddress().c_str());
+ // fx goes out of scope and strong ref on AudioEffect is released
+ continue;
+ }
+ fx->setEnabled(true);
+ ALOGV("%s(): create Fx %s added on port type=%d address=%s", __func__,
+ effectDesc->mName, deviceEffects->getDeviceType(),
+ deviceEffects->getDeviceAddress().c_str());
+ deviceEffects->mEffects.push_back(std::move(fx));
+ }
+ }
+}
} // namespace android
diff --git a/services/audiopolicy/service/AudioPolicyEffects.h b/services/audiopolicy/service/AudioPolicyEffects.h
index dcf093b..81c728d 100644
--- a/services/audiopolicy/service/AudioPolicyEffects.h
+++ b/services/audiopolicy/service/AudioPolicyEffects.h
@@ -25,6 +25,9 @@
#include <system/audio.h>
#include <utils/Vector.h>
#include <utils/SortedVector.h>
+#include <android-base/thread_annotations.h>
+
+#include <future>
namespace android {
@@ -104,6 +107,7 @@
status_t removeStreamDefaultEffect(audio_unique_id_t id);
private:
+ void initDefaultDeviceEffects();
// class to store the description of an effects and its parameters
// as defined in audio_effects.conf
@@ -192,6 +196,28 @@
Vector< sp<AudioEffect> >mEffects;
};
+ /**
+ * @brief The DeviceEffects class stores the effects associated to a given Device Port.
+ */
+ class DeviceEffects {
+ public:
+ DeviceEffects(std::unique_ptr<EffectDescVector> effectDescriptors,
+ audio_devices_t device, const std::string& address) :
+ mEffectDescriptors(std::move(effectDescriptors)),
+ mDeviceType(device), mDeviceAddress(address) {}
+ /*virtual*/ ~DeviceEffects() = default;
+
+ std::vector<std::unique_ptr<AudioEffect>> mEffects;
+ audio_devices_t getDeviceType() const { return mDeviceType; }
+ std::string getDeviceAddress() const { return mDeviceAddress; }
+ const std::unique_ptr<EffectDescVector> mEffectDescriptors;
+
+ private:
+ const audio_devices_t mDeviceType;
+ const std::string mDeviceAddress;
+
+ };
+
static const char * const kInputSourceNames[AUDIO_SOURCE_CNT -1];
static audio_source_t inputSourceNameToEnum(const char *name);
@@ -237,6 +263,19 @@
KeyedVector< audio_stream_type_t, EffectDescVector* > mOutputStreams;
// Automatic output effects are unique for audiosession ID
KeyedVector< audio_session_t, EffectVector* > mOutputSessions;
+
+ /**
+ * @brief mDeviceEffects map of device effects indexed by the device address
+ */
+ std::map<std::string, std::unique_ptr<DeviceEffects>> mDeviceEffects GUARDED_BY(mLock);
+
+ /**
+ * Device Effect initialization must be asynchronous: the audio_policy service parses and init
+ * effect on first reference. AudioFlinger will handle effect creation and register these
+ * effect on audio_policy service.
+ * We must store the reference of the furture garantee real asynchronous operation.
+ */
+ std::future<void> mDefaultDeviceEffectFuture;
};
} // namespace android
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 6379a8a..8bc2837 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -19,12 +19,50 @@
#include "AudioPolicyService.h"
#include "TypeConverter.h"
-#include <media/MediaAnalyticsItem.h>
+#include <media/MediaMetricsItem.h>
#include <media/AudioPolicy.h>
#include <utils/Log.h>
namespace android {
+const std::vector<audio_usage_t>& SYSTEM_USAGES = {
+ AUDIO_USAGE_CALL_ASSISTANT,
+ AUDIO_USAGE_EMERGENCY,
+ AUDIO_USAGE_SAFETY,
+ AUDIO_USAGE_VEHICLE_STATUS,
+ AUDIO_USAGE_ANNOUNCEMENT
+};
+
+bool isSystemUsage(audio_usage_t usage) {
+ return std::find(std::begin(SYSTEM_USAGES), std::end(SYSTEM_USAGES), usage)
+ != std::end(SYSTEM_USAGES);
+}
+
+bool AudioPolicyService::isSupportedSystemUsage(audio_usage_t usage) {
+ return std::find(std::begin(mSupportedSystemUsages), std::end(mSupportedSystemUsages), usage)
+ != std::end(mSupportedSystemUsages);
+}
+
+status_t AudioPolicyService::validateUsage(audio_usage_t usage) {
+ return validateUsage(usage, IPCThreadState::self()->getCallingPid(),
+ IPCThreadState::self()->getCallingUid());
+}
+
+status_t AudioPolicyService::validateUsage(audio_usage_t usage, pid_t pid, uid_t uid) {
+ if (isSystemUsage(usage)) {
+ if (isSupportedSystemUsage(usage)) {
+ if (!modifyAudioRoutingAllowed(pid, uid)) {
+ ALOGE("permission denied: modify audio routing not allowed for uid %d", uid);
+ return PERMISSION_DENIED;
+ }
+ } else {
+ return BAD_VALUE;
+ }
+ }
+ return NO_ERROR;
+}
+
+
// ----------------------------------------------------------------------------
@@ -181,6 +219,12 @@
if (mAudioPolicyManager == NULL) {
return NO_INIT;
}
+
+ status_t result = validateUsage(attr->usage, pid, uid);
+ if (result != NO_ERROR) {
+ return result;
+ }
+
ALOGV("%s()", __func__);
Mutex::Autolock _l(mLock);
@@ -199,7 +243,7 @@
}
AutoCallerClear acc;
AudioPolicyInterface::output_type_t outputType;
- status_t result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid,
+ result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid,
config,
&flags, selectedDeviceId, portId,
secondaryOutputs,
@@ -363,12 +407,22 @@
return NO_INIT;
}
+ status_t result = validateUsage(attr->usage, pid, uid);
+ if (result != NO_ERROR) {
+ return result;
+ }
+
+ audio_source_t inputSource = attr->source;
+ if (inputSource == AUDIO_SOURCE_DEFAULT) {
+ inputSource = AUDIO_SOURCE_MIC;
+ }
+
// already checked by client, but double-check in case the client wrapper is bypassed
- if ((attr->source < AUDIO_SOURCE_DEFAULT)
- || (attr->source >= AUDIO_SOURCE_CNT
- && attr->source != AUDIO_SOURCE_HOTWORD
- && attr->source != AUDIO_SOURCE_FM_TUNER
- && attr->source != AUDIO_SOURCE_ECHO_REFERENCE)) {
+ if ((inputSource < AUDIO_SOURCE_DEFAULT)
+ || (inputSource >= AUDIO_SOURCE_CNT
+ && inputSource != AUDIO_SOURCE_HOTWORD
+ && inputSource != AUDIO_SOURCE_FM_TUNER
+ && inputSource != AUDIO_SOURCE_ECHO_REFERENCE)) {
return BAD_VALUE;
}
@@ -399,17 +453,17 @@
}
bool canCaptureOutput = captureAudioOutputAllowed(pid, uid);
- if ((attr->source == AUDIO_SOURCE_VOICE_UPLINK ||
- attr->source == AUDIO_SOURCE_VOICE_DOWNLINK ||
- attr->source == AUDIO_SOURCE_VOICE_CALL ||
- attr->source == AUDIO_SOURCE_ECHO_REFERENCE||
- attr->source == AUDIO_SOURCE_FM_TUNER) &&
+ if ((inputSource == AUDIO_SOURCE_VOICE_UPLINK ||
+ inputSource == AUDIO_SOURCE_VOICE_DOWNLINK ||
+ inputSource == AUDIO_SOURCE_VOICE_CALL ||
+ inputSource == AUDIO_SOURCE_ECHO_REFERENCE||
+ inputSource == AUDIO_SOURCE_FM_TUNER) &&
!canCaptureOutput) {
return PERMISSION_DENIED;
}
bool canCaptureHotword = captureHotwordAllowed(opPackageName, pid, uid);
- if ((attr->source == AUDIO_SOURCE_HOTWORD) && !canCaptureHotword) {
+ if ((inputSource == AUDIO_SOURCE_HOTWORD) && !canCaptureHotword) {
return BAD_VALUE;
}
@@ -474,7 +528,7 @@
if (audioPolicyEffects != 0) {
// create audio pre processors according to input source
- status_t status = audioPolicyEffects->addInputEffects(*input, attr->source, session);
+ status_t status = audioPolicyEffects->addInputEffects(*input, inputSource, session);
if (status != NO_ERROR && status != ALREADY_EXISTS) {
ALOGW("Failed to add effects on input %d", *input);
}
@@ -548,7 +602,7 @@
static constexpr char kAudioPolicyActiveDevice[] =
"android.media.audiopolicy.active.device";
- MediaAnalyticsItem *item = MediaAnalyticsItem::create(kAudioPolicy);
+ mediametrics::Item *item = mediametrics::Item::create(kAudioPolicy);
if (item != NULL) {
item->setInt32(kAudioPolicyStatus, status);
@@ -801,6 +855,17 @@
return mAudioPolicyManager->getDevicesForStream(stream);
}
+status_t AudioPolicyService::getDevicesForAttributes(const AudioAttributes &aa,
+ AudioDeviceTypeAddrVector *devices) const
+{
+ if (mAudioPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ Mutex::Autolock _l(mLock);
+ AutoCallerClear acc;
+ return mAudioPolicyManager->getDevicesForAttributes(aa.getAttributes(), devices);
+}
+
audio_io_handle_t AudioPolicyService::getOutputForEffect(const effect_descriptor_t *desc)
{
// FIXME change return type to status_t, and return NO_INIT here
@@ -986,17 +1051,28 @@
return audioPolicyEffects->removeStreamDefaultEffect(id);
}
+status_t AudioPolicyService::setSupportedSystemUsages(const std::vector<audio_usage_t>& systemUsages) {
+ Mutex::Autolock _l(mLock);
+ if(!modifyAudioRoutingAllowed()) {
+ return PERMISSION_DENIED;
+ }
+
+ bool areAllSystemUsages = std::all_of(begin(systemUsages), end(systemUsages),
+ [](audio_usage_t usage) { return isSystemUsage(usage); });
+ if (!areAllSystemUsages) {
+ return BAD_VALUE;
+ }
+
+ mSupportedSystemUsages = systemUsages;
+ return NO_ERROR;
+}
+
status_t AudioPolicyService::setAllowedCapturePolicy(uid_t uid, audio_flags_mask_t capturePolicy) {
Mutex::Autolock _l(mLock);
if (mAudioPolicyManager == NULL) {
ALOGV("%s() mAudioPolicyManager == NULL", __func__);
return NO_INIT;
}
- uint_t callingUid = IPCThreadState::self()->getCallingUid();
- if (uid != callingUid) {
- ALOGD("%s() uid invalid %d != %d", __func__, uid, callingUid);
- return PERMISSION_DENIED;
- }
return mAudioPolicyManager->setAllowedCapturePolicy(uid, capturePolicy);
}
@@ -1017,6 +1093,12 @@
ALOGV("mAudioPolicyManager == NULL");
return false;
}
+
+ status_t result = validateUsage(attributes.usage);
+ if (result != NO_ERROR) {
+ return result;
+ }
+
Mutex::Autolock _l(mLock);
return mAudioPolicyManager->isDirectOutputSupported(config, attributes);
}
@@ -1133,14 +1215,39 @@
return PERMISSION_DENIED;
}
+ // Require CAPTURE_VOICE_COMMUNICATION_OUTPUT if one of the
+ // mixes is a render|loopback mix that aim to capture audio played with
+ // USAGE_VOICE_COMMUNICATION.
+ bool needCaptureVoiceCommunicationOutput =
+ std::any_of(mixes.begin(), mixes.end(), [](auto& mix) {
+ return is_mix_loopback_render(mix.mRouteFlags) &&
+ mix.hasMatchingRuleForUsage([] (auto usage) {
+ return usage == AUDIO_USAGE_VOICE_COMMUNICATION;});
+ });
+
+ // Require CAPTURE_MEDIA_OUTPUT if there is a mix for priveliged capture
+ // which is trying to capture any usage which is not USAGE_VOICE_COMMUNICATION.
+ // (If USAGE_VOICE_COMMUNICATION should be captured, then CAPTURE_VOICE_COMMUNICATION_OUTPUT
+ // is required, even if it is not privileged capture).
bool needCaptureMediaOutput = std::any_of(mixes.begin(), mixes.end(), [](auto& mix) {
- return mix.mAllowPrivilegedPlaybackCapture; });
+ return mix.mAllowPrivilegedPlaybackCapture &&
+ mix.hasMatchingRuleForUsage([] (auto usage) {
+ return usage != AUDIO_USAGE_VOICE_COMMUNICATION;
+ });
+ });
+
const uid_t callingUid = IPCThreadState::self()->getCallingUid();
const pid_t callingPid = IPCThreadState::self()->getCallingPid();
+
if (needCaptureMediaOutput && !captureMediaOutputAllowed(callingPid, callingUid)) {
return PERMISSION_DENIED;
}
+ if (needCaptureVoiceCommunicationOutput &&
+ !captureVoiceCommunicationOutputAllowed(callingPid, callingUid)) {
+ return PERMISSION_DENIED;
+ }
+
if (mAudioPolicyManager == NULL) {
return NO_INIT;
}
@@ -1177,6 +1284,31 @@
return mAudioPolicyManager->removeUidDeviceAffinities(uid);
}
+status_t AudioPolicyService::setUserIdDeviceAffinities(int userId,
+ const Vector<AudioDeviceTypeAddr>& devices) {
+ Mutex::Autolock _l(mLock);
+ if(!modifyAudioRoutingAllowed()) {
+ return PERMISSION_DENIED;
+ }
+ if (mAudioPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ AutoCallerClear acc;
+ return mAudioPolicyManager->setUserIdDeviceAffinities(userId, devices);
+}
+
+status_t AudioPolicyService::removeUserIdDeviceAffinities(int userId) {
+ Mutex::Autolock _l(mLock);
+ if(!modifyAudioRoutingAllowed()) {
+ return PERMISSION_DENIED;
+ }
+ if (mAudioPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ AutoCallerClear acc;
+ return mAudioPolicyManager->removeUserIdDeviceAffinities(userId);
+}
+
status_t AudioPolicyService::startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,
audio_port_handle_t *portId)
@@ -1185,6 +1317,12 @@
if (mAudioPolicyManager == NULL) {
return NO_INIT;
}
+
+ status_t result = validateUsage(attributes->usage);
+ if (result != NO_ERROR) {
+ return result;
+ }
+
// startAudioSource should be created as the calling uid
const uid_t callingUid = IPCThreadState::self()->getCallingUid();
AutoCallerClear acc;
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index c83512f..e5c36ea 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -74,6 +74,8 @@
mAudioPolicyClient = new AudioPolicyClient(this);
mAudioPolicyManager = createAudioPolicyManager(mAudioPolicyClient);
+
+ mSupportedSystemUsages = std::vector<audio_usage_t> {};
}
// load audio processing modules
sp<AudioPolicyEffects>audioPolicyEffects = new AudioPolicyEffects();
@@ -392,6 +394,14 @@
snprintf(buffer, SIZE, "Command Thread: %p\n", mAudioCommandThread.get());
result.append(buffer);
+ snprintf(buffer, SIZE, "Supported System Usages:\n");
+ result.append(buffer);
+ for (std::vector<audio_usage_t>::iterator it = mSupportedSystemUsages.begin();
+ it != mSupportedSystemUsages.end(); ++it) {
+ snprintf(buffer, SIZE, "\t%d\n", *it);
+ result.append(buffer);
+ }
+
write(fd, result.string(), result.size());
return NO_ERROR;
}
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index c6fff63..e297f34 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -127,6 +127,8 @@
virtual uint32_t getStrategyForStream(audio_stream_type_t stream);
virtual audio_devices_t getDevicesForStream(audio_stream_type_t stream);
+ virtual status_t getDevicesForAttributes(const AudioAttributes &aa,
+ AudioDeviceTypeAddrVector *devices) const;
virtual audio_io_handle_t getOutputForEffect(const effect_descriptor_t *desc);
virtual status_t registerEffect(const effect_descriptor_t *desc,
@@ -185,6 +187,7 @@
audio_io_handle_t output,
int delayMs = 0);
virtual status_t setVoiceVolume(float volume, int delayMs = 0);
+ status_t setSupportedSystemUsages(const std::vector<audio_usage_t>& systemUsages);
status_t setAllowedCapturePolicy(uint_t uid, audio_flags_mask_t capturePolicy) override;
virtual bool isOffloadSupported(const audio_offload_info_t &config);
virtual bool isDirectOutputSupported(const audio_config_base_t& config,
@@ -232,6 +235,9 @@
virtual status_t getPreferredDeviceForStrategy(product_strategy_t strategy,
AudioDeviceTypeAddr &device);
+ virtual status_t setUserIdDeviceAffinities(int userId, const Vector<AudioDeviceTypeAddr>& devices);
+
+ virtual status_t removeUserIdDeviceAffinities(int userId);
virtual status_t startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,
@@ -342,6 +348,10 @@
app_state_t apmStatFromAmState(int amState);
+ bool isSupportedSystemUsage(audio_usage_t usage);
+ status_t validateUsage(audio_usage_t usage);
+ status_t validateUsage(audio_usage_t usage, pid_t pid, uid_t uid);
+
void updateUidStates();
void updateUidStates_l();
@@ -717,6 +727,8 @@
virtual audio_unique_id_t newAudioUniqueId(audio_unique_id_use_t use);
+ void setSoundTriggerCaptureState(bool active) override;
+
private:
AudioPolicyService *mAudioPolicyService;
};
@@ -859,6 +871,7 @@
struct audio_policy *mpAudioPolicy;
AudioPolicyInterface *mAudioPolicyManager;
AudioPolicyClient *mAudioPolicyClient;
+ std::vector<audio_usage_t> mSupportedSystemUsages;
DefaultKeyedVector< int64_t, sp<NotificationClient> > mNotificationClients;
Mutex mNotificationClientsLock; // protects mNotificationClients
diff --git a/services/audiopolicy/service/BinderProxy.h b/services/audiopolicy/service/BinderProxy.h
new file mode 100644
index 0000000..516e84d
--- /dev/null
+++ b/services/audiopolicy/service/BinderProxy.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2019 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 <mutex>
+#include <type_traits>
+#include <binder/IInterface.h>
+#include <binder/IServiceManager.h>
+#include <utils/Log.h>
+
+namespace android {
+
+// A simple utility that caches a proxy for a service and handles death notification.
+// Typically, intended to be used as a static-lifetime object.
+//
+// Example usage:
+// static BinderProxy<IMyInterface> myInterface("my_interface_svc");
+// ...
+// myInterface.waitServiceOrDie()->doSomething();
+//
+// If the service is unavailable, will wait until it becomes available.
+// Will die if the service doesn't implement the requested interface, or cannot be used for
+// permission reasons.
+template<typename ServiceType>
+class BinderProxy {
+public:
+ static_assert(std::is_base_of_v<IInterface, ServiceType>,
+ "Service type must be a sub-type of IInterface.");
+
+ explicit BinderProxy(std::string_view serviceName)
+ : mServiceName(serviceName), mDeathRecipient(new DeathRecipient(this)) {}
+
+ ~BinderProxy() {
+ if (mDelegate != nullptr) {
+ sp<IBinder> binder = IInterface::asBinder(mDelegate);
+ if (binder != nullptr) {
+ binder->unlinkToDeath(mDeathRecipient);
+ }
+ }
+ }
+
+ sp<ServiceType> waitServiceOrDie() {
+ std::lock_guard<std::mutex> _l(mDelegateMutex);
+ if (mDelegate == nullptr) {
+ mDelegate = waitForService<ServiceType>(String16(mServiceName.c_str()));
+ LOG_ALWAYS_FATAL_IF(mDelegate == nullptr,
+ "Service %s doesn't implement the required interface.",
+ mServiceName.c_str());
+ sp<IBinder> binder = IInterface::asBinder(mDelegate);
+ if (binder != nullptr) {
+ binder->linkToDeath(mDeathRecipient);
+ }
+ }
+ return mDelegate;
+ }
+
+private:
+ sp<ServiceType> mDelegate;
+ std::mutex mDelegateMutex;
+ const std::string mServiceName;
+ sp<IBinder::DeathRecipient> mDeathRecipient;
+
+ class DeathRecipient : public IBinder::DeathRecipient {
+ public:
+ DeathRecipient(BinderProxy* proxy) : mProxy(proxy) {}
+
+ void binderDied(const wp<IBinder>&) override {
+ mProxy->binderDied();
+ }
+
+ private:
+ BinderProxy* const mProxy;
+ };
+
+ void binderDied() {
+ std::lock_guard<std::mutex> _l(mDelegateMutex);
+ mDelegate.clear();
+ ALOGW("Binder died: %s", mServiceName.c_str());
+ }
+};
+
+} // namespace android
diff --git a/services/audiopolicy/tests/AudioPolicyTestClient.h b/services/audiopolicy/tests/AudioPolicyTestClient.h
index b92a2e6..c628e70 100644
--- a/services/audiopolicy/tests/AudioPolicyTestClient.h
+++ b/services/audiopolicy/tests/AudioPolicyTestClient.h
@@ -86,6 +86,7 @@
void setEffectSuspended(int effectId __unused,
audio_session_t sessionId __unused,
bool suspended __unused) {}
+ void setSoundTriggerCaptureState(bool active __unused) override {};
};
} // namespace android
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index e4a19ea..2a8349c 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -800,6 +800,8 @@
(audio_attributes_t){AUDIO_CONTENT_TYPE_MUSIC, AUDIO_USAGE_VIRTUAL_SOURCE,
AUDIO_SOURCE_DEFAULT, 0, "addr=remote_submix_media"},
(audio_attributes_t){AUDIO_CONTENT_TYPE_MUSIC, AUDIO_USAGE_ASSISTANT,
+ AUDIO_SOURCE_DEFAULT, 0, "addr=remote_submix_media"},
+ (audio_attributes_t){AUDIO_CONTENT_TYPE_SPEECH, AUDIO_USAGE_ASSISTANT,
AUDIO_SOURCE_DEFAULT, 0, "addr=remote_submix_media"}
)
);
@@ -841,6 +843,8 @@
(audio_attributes_t){AUDIO_CONTENT_TYPE_MUSIC, AUDIO_USAGE_GAME,
AUDIO_SOURCE_DEFAULT, 0, ""},
(audio_attributes_t){AUDIO_CONTENT_TYPE_MUSIC, AUDIO_USAGE_ASSISTANT,
+ AUDIO_SOURCE_DEFAULT, 0, ""},
+ (audio_attributes_t){AUDIO_CONTENT_TYPE_SPEECH, AUDIO_USAGE_ASSISTANT,
AUDIO_SOURCE_DEFAULT, 0, ""}
)
);
diff --git a/services/camera/libcameraservice/Android.bp b/services/camera/libcameraservice/Android.bp
index c50a3c6..98fee12 100644
--- a/services/camera/libcameraservice/Android.bp
+++ b/services/camera/libcameraservice/Android.bp
@@ -26,7 +26,9 @@
"CameraFlashlight.cpp",
"common/Camera2ClientBase.cpp",
"common/CameraDeviceBase.cpp",
+ "common/CameraOfflineSessionBase.cpp",
"common/CameraProviderManager.cpp",
+ "common/DepthPhotoProcessor.cpp",
"common/FrameProcessorBase.cpp",
"api1/CameraClient.cpp",
"api1/Camera2Client.cpp",
@@ -39,12 +41,15 @@
"api1/client2/CaptureSequencer.cpp",
"api1/client2/ZslProcessor.cpp",
"api2/CameraDeviceClient.cpp",
+ "api2/CameraOfflineSessionClient.cpp",
"api2/CompositeStream.cpp",
"api2/DepthCompositeStream.cpp",
"api2/HeicEncoderInfoManager.cpp",
"api2/HeicCompositeStream.cpp",
"device1/CameraHardwareInterface.cpp",
+ "device3/BufferUtils.cpp",
"device3/Camera3Device.cpp",
+ "device3/Camera3OfflineSession.cpp",
"device3/Camera3Stream.cpp",
"device3/Camera3IOStreamBase.cpp",
"device3/Camera3InputStream.cpp",
@@ -54,23 +59,30 @@
"device3/StatusTracker.cpp",
"device3/Camera3BufferManager.cpp",
"device3/Camera3StreamSplitter.cpp",
+ "device3/CoordinateMapper.cpp",
"device3/DistortionMapper.cpp",
+ "device3/ZoomRatioMapper.cpp",
+ "device3/RotateAndCropMapper.cpp",
+ "device3/Camera3OutputStreamInterface.cpp",
+ "device3/Camera3OutputUtils.cpp",
"gui/RingBufferConsumer.cpp",
- "utils/CameraThreadState.cpp",
"hidl/AidlCameraDeviceCallbacks.cpp",
"hidl/AidlCameraServiceListener.cpp",
"hidl/Convert.cpp",
"hidl/HidlCameraDeviceUser.cpp",
"hidl/HidlCameraService.cpp",
+ "utils/CameraThreadState.cpp",
"utils/CameraTraces.cpp",
"utils/AutoConditionLock.cpp",
"utils/ExifUtils.cpp",
+ "utils/SessionConfigurationUtils.cpp",
"utils/TagMonitor.cpp",
"utils/LatencyHistogram.cpp",
],
header_libs: [
- "libmediadrm_headers"
+ "libmediadrm_headers",
+ "libmediametrics_headers",
],
shared_libs: [
@@ -87,10 +99,12 @@
"libmediautils",
"libcamera_client",
"libcamera_metadata",
+ "libdynamic_depth",
"libfmq",
"libgui",
"libhardware",
"libhidlbase",
+ "libimage_io",
"libjpeg",
"libmedia_codeclist",
"libmedia_omx",
@@ -98,6 +112,7 @@
"libsensorprivacy",
"libstagefright",
"libstagefright_foundation",
+ "libxml2",
"libyuv",
"android.frameworks.cameraservice.common@2.0",
"android.frameworks.cameraservice.service@2.0",
@@ -105,11 +120,13 @@
"android.hardware.camera.common@1.0",
"android.hardware.camera.provider@2.4",
"android.hardware.camera.provider@2.5",
+ "android.hardware.camera.provider@2.6",
"android.hardware.camera.device@1.0",
"android.hardware.camera.device@3.2",
"android.hardware.camera.device@3.3",
"android.hardware.camera.device@3.4",
"android.hardware.camera.device@3.5",
+ "android.hardware.camera.device@3.6"
],
export_shared_lib_headers: [
@@ -138,40 +155,3 @@
}
-cc_library_shared {
- name: "libdepthphoto",
-
- srcs: [
- "utils/ExifUtils.cpp",
- "common/DepthPhotoProcessor.cpp",
- ],
-
- shared_libs: [
- "libimage_io",
- "libdynamic_depth",
- "libxml2",
- "liblog",
- "libutilscallstack",
- "libutils",
- "libcutils",
- "libjpeg",
- "libmemunreachable",
- "libexif",
- "libcamera_client",
- ],
-
- include_dirs: [
- "external/dynamic_depth/includes",
- "external/dynamic_depth/internal",
- ],
-
- export_include_dirs: ["."],
-
- cflags: [
- "-Wall",
- "-Wextra",
- "-Werror",
- "-Wno-ignored-qualifiers",
- ],
-
-}
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index b704573..5358307 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -56,12 +56,12 @@
#include <media/IMediaHTTPService.h>
#include <media/mediaplayer.h>
#include <mediautils/BatteryNotifier.h>
-#include <sensorprivacy/SensorPrivacyManager.h>
#include <utils/Errors.h>
#include <utils/Log.h>
#include <utils/String16.h>
#include <utils/SystemClock.h>
#include <utils/Trace.h>
+#include <utils/CallStack.h>
#include <private/android_filesystem_config.h>
#include <system/camera_vendor_tags.h>
#include <system/camera_metadata.h>
@@ -91,6 +91,8 @@
using hardware::ICameraServiceListener;
using hardware::camera::common::V1_0::CameraDeviceStatus;
using hardware::camera::common::V1_0::TorchModeStatus;
+using hardware::camera2::utils::CameraIdAndSessionConfiguration;
+using hardware::camera2::utils::ConcurrentCameraIdCombination;
// ----------------------------------------------------------------------------
// Logging support -- this is for debugging only
@@ -128,6 +130,7 @@
static constexpr int32_t kVendorClientScore = 200;
// Matches with PROCESS_STATE_PERSISTENT_UI in ActivityManager.java
static constexpr int32_t kVendorClientState = 1;
+const String8 CameraService::kOfflineDevice("offline-");
Mutex CameraService::sProxyMutex;
sp<hardware::ICameraServiceProxy> CameraService::sCameraServiceProxy;
@@ -394,7 +397,7 @@
// to this device until the status changes
updateStatus(StatusInternal::NOT_PRESENT, id);
- sp<BasicClient> clientToDisconnect;
+ sp<BasicClient> clientToDisconnectOnline, clientToDisconnectOffline;
{
// Don't do this in updateStatus to avoid deadlock over mServiceLock
Mutex::Autolock lock(mServiceLock);
@@ -402,23 +405,14 @@
// Remove cached shim parameters
state->setShimParams(CameraParameters());
- // Remove the client from the list of active clients, if there is one
- clientToDisconnect = removeClientLocked(id);
+ // Remove online as well as offline client from the list of active clients,
+ // if they are present
+ clientToDisconnectOnline = removeClientLocked(id);
+ clientToDisconnectOffline = removeClientLocked(kOfflineDevice + id);
}
- // Disconnect client
- if (clientToDisconnect.get() != nullptr) {
- ALOGI("%s: Client for camera ID %s evicted due to device status change from HAL",
- __FUNCTION__, id.string());
- // Notify the client of disconnection
- clientToDisconnect->notifyError(
- hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DISCONNECTED,
- CaptureResultExtras{});
- // Ensure not in binder RPC so client disconnect PID checks work correctly
- LOG_ALWAYS_FATAL_IF(CameraThreadState::getCallingPid() != getpid(),
- "onDeviceStatusChanged must be called from the camera service process!");
- clientToDisconnect->disconnect();
- }
+ disconnectClient(id, clientToDisconnectOnline);
+ disconnectClient(kOfflineDevice + id, clientToDisconnectOffline);
removeStates(id);
} else {
@@ -428,7 +422,67 @@
}
updateStatus(newStatus, id);
}
+}
+void CameraService::onDeviceStatusChanged(const String8& id,
+ const String8& physicalId,
+ CameraDeviceStatus newHalStatus) {
+ ALOGI("%s: Status changed for cameraId=%s, physicalCameraId=%s, newStatus=%d",
+ __FUNCTION__, id.string(), physicalId.string(), newHalStatus);
+
+ StatusInternal newStatus = mapToInternal(newHalStatus);
+
+ std::shared_ptr<CameraState> state = getCameraState(id);
+
+ if (state == nullptr) {
+ ALOGE("%s: Physical camera id %s status change on a non-present ID %s",
+ __FUNCTION__, id.string(), physicalId.string());
+ return;
+ }
+
+ StatusInternal logicalCameraStatus = state->getStatus();
+ if (logicalCameraStatus != StatusInternal::PRESENT &&
+ logicalCameraStatus != StatusInternal::NOT_AVAILABLE) {
+ ALOGE("%s: Physical camera id %s status %d change for an invalid logical camera state %d",
+ __FUNCTION__, physicalId.string(), newHalStatus, logicalCameraStatus);
+ return;
+ }
+
+ bool updated = false;
+ if (newStatus == StatusInternal::PRESENT) {
+ updated = state->removeUnavailablePhysicalId(physicalId);
+ } else {
+ updated = state->addUnavailablePhysicalId(physicalId);
+ }
+
+ if (updated) {
+ logDeviceRemoved(id, String8::format("Device %s-%s availability changed from %d to %d",
+ id.string(), physicalId.string(),
+ newStatus != StatusInternal::PRESENT,
+ newStatus == StatusInternal::PRESENT));
+
+ String16 id16(id), physicalId16(physicalId);
+ Mutex::Autolock lock(mStatusListenerLock);
+ for (auto& listener : mListenerList) {
+ listener->getListener()->onPhysicalCameraStatusChanged(mapToInterface(newStatus),
+ id16, physicalId16);
+ }
+ }
+}
+
+void CameraService::disconnectClient(const String8& id, sp<BasicClient> clientToDisconnect) {
+ if (clientToDisconnect.get() != nullptr) {
+ ALOGI("%s: Client for camera ID %s evicted due to device status change from HAL",
+ __FUNCTION__, id.string());
+ // Notify the client of disconnection
+ clientToDisconnect->notifyError(
+ hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DISCONNECTED,
+ CaptureResultExtras{});
+ // Ensure not in binder RPC so client disconnect PID checks work correctly
+ LOG_ALWAYS_FATAL_IF(CameraThreadState::getCallingPid() != getpid(),
+ "onDeviceStatusChanged must be called from the camera service process!");
+ clientToDisconnect->disconnect();
+ }
}
void CameraService::onTorchStatusChanged(const String8& cameraId,
@@ -761,6 +815,7 @@
case CAMERA_DEVICE_API_VERSION_3_3:
case CAMERA_DEVICE_API_VERSION_3_4:
case CAMERA_DEVICE_API_VERSION_3_5:
+ case CAMERA_DEVICE_API_VERSION_3_6:
if (effectiveApiLevel == API_1) { // Camera1 API route
sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
*client = new Camera2Client(cameraService, tmp, packageName, featureId,
@@ -1678,6 +1733,11 @@
}
}
+ // Set rotate-and-crop override behavior
+ if (mOverrideRotateAndCropMode != ANDROID_SCALER_ROTATE_AND_CROP_AUTO) {
+ client->setRotateAndCropOverride(mOverrideRotateAndCropMode);
+ }
+
if (shimUpdateOnly) {
// If only updating legacy shim parameters, immediately disconnect client
mServiceLock.unlock();
@@ -1695,6 +1755,77 @@
return ret;
}
+status_t CameraService::addOfflineClient(String8 cameraId, sp<BasicClient> offlineClient) {
+ if (offlineClient.get() == nullptr) {
+ return BAD_VALUE;
+ }
+
+ {
+ // Acquire mServiceLock and prevent other clients from connecting
+ std::unique_ptr<AutoConditionLock> lock =
+ AutoConditionLock::waitAndAcquire(mServiceLockWrapper, DEFAULT_CONNECT_TIMEOUT_NS);
+
+ if (lock == nullptr) {
+ ALOGE("%s: (PID %d) rejected (too many other clients connecting)."
+ , __FUNCTION__, offlineClient->getClientPid());
+ return TIMED_OUT;
+ }
+
+ auto onlineClientDesc = mActiveClientManager.get(cameraId);
+ if (onlineClientDesc.get() == nullptr) {
+ ALOGE("%s: No active online client using camera id: %s", __FUNCTION__,
+ cameraId.c_str());
+ return BAD_VALUE;
+ }
+
+ // Offline clients do not evict or conflict with other online devices. Resource sharing
+ // conflicts are handled by the camera provider which will either succeed or fail before
+ // reaching this method.
+ const auto& onlinePriority = onlineClientDesc->getPriority();
+ auto offlineClientDesc = CameraClientManager::makeClientDescriptor(
+ kOfflineDevice + onlineClientDesc->getKey(), offlineClient, /*cost*/ 0,
+ /*conflictingKeys*/ std::set<String8>(), onlinePriority.getScore(),
+ onlineClientDesc->getOwnerId(), onlinePriority.getState());
+
+ // Allow only one offline device per camera
+ auto incompatibleClients = mActiveClientManager.getIncompatibleClients(offlineClientDesc);
+ if (!incompatibleClients.empty()) {
+ ALOGE("%s: Incompatible offline clients present!", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ auto err = offlineClient->initialize(mCameraProviderManager, mMonitorTags);
+ if (err != OK) {
+ ALOGE("%s: Could not initialize offline client.", __FUNCTION__);
+ return err;
+ }
+
+ auto evicted = mActiveClientManager.addAndEvict(offlineClientDesc);
+ if (evicted.size() > 0) {
+ for (auto& i : evicted) {
+ ALOGE("%s: Invalid state: Offline client for camera %s was not removed ",
+ __FUNCTION__, i->getKey().string());
+ }
+
+ LOG_ALWAYS_FATAL("%s: Invalid state for CameraService, offline clients not evicted "
+ "properly", __FUNCTION__);
+
+ return BAD_VALUE;
+ }
+
+ logConnectedOffline(offlineClientDesc->getKey(),
+ static_cast<int>(offlineClientDesc->getOwnerId()),
+ String8(offlineClient->getPackageName()));
+
+ sp<IBinder> remoteCallback = offlineClient->getRemote();
+ if (remoteCallback != nullptr) {
+ remoteCallback->linkToDeath(this);
+ }
+ } // lock is destroyed, allow further connect calls
+
+ return OK;
+}
+
Status CameraService::setTorchMode(const String16& cameraId, bool enabled,
const sp<IBinder>& clientBinder) {
Mutex::Autolock lock(mServiceLock);
@@ -1911,6 +2042,83 @@
return Status::ok();
}
+ Status CameraService::getConcurrentStreamingCameraIds(
+ std::vector<ConcurrentCameraIdCombination>* concurrentCameraIds) {
+ ATRACE_CALL();
+ if (!concurrentCameraIds) {
+ ALOGE("%s: concurrentCameraIds is NULL", __FUNCTION__);
+ return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "concurrentCameraIds is NULL");
+ }
+
+ if (!mInitialized) {
+ ALOGE("%s: Camera HAL couldn't be initialized", __FUNCTION__);
+ return STATUS_ERROR(ERROR_DISCONNECTED,
+ "Camera subsystem is not available");
+ }
+ // First call into the provider and get the set of concurrent camera
+ // combinations
+ std::vector<std::unordered_set<std::string>> concurrentCameraCombinations =
+ mCameraProviderManager->getConcurrentStreamingCameraIds();
+ for (auto &combination : concurrentCameraCombinations) {
+ std::vector<std::string> validCombination;
+ for (auto &cameraId : combination) {
+ // if the camera state is not present, skip
+ String8 cameraIdStr(cameraId.c_str());
+ auto state = getCameraState(cameraIdStr);
+ if (state == nullptr) {
+ ALOGW("%s: camera id %s does not exist", __FUNCTION__, cameraId.c_str());
+ continue;
+ }
+ StatusInternal status = state->getStatus();
+ if (status == StatusInternal::NOT_PRESENT || status == StatusInternal::ENUMERATING) {
+ continue;
+ }
+ if (shouldRejectSystemCameraConnection(cameraIdStr)) {
+ continue;
+ }
+ validCombination.push_back(cameraId);
+ }
+ if (validCombination.size() != 0) {
+ concurrentCameraIds->push_back(std::move(validCombination));
+ }
+ }
+ return Status::ok();
+}
+
+Status CameraService::isConcurrentSessionConfigurationSupported(
+ const std::vector<CameraIdAndSessionConfiguration>& cameraIdsAndSessionConfigurations,
+ /*out*/bool* isSupported) {
+ if (!isSupported) {
+ ALOGE("%s: isSupported is NULL", __FUNCTION__);
+ return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "isSupported is NULL");
+ }
+
+ if (!mInitialized) {
+ ALOGE("%s: Camera HAL couldn't be initialized", __FUNCTION__);
+ return STATUS_ERROR(ERROR_DISCONNECTED,
+ "Camera subsystem is not available");
+ }
+
+ // Check for camera permissions
+ int callingPid = CameraThreadState::getCallingPid();
+ int callingUid = CameraThreadState::getCallingUid();
+ if ((callingPid != getpid()) && !checkPermission(sCameraPermission, callingPid, callingUid)) {
+ ALOGE("%s: pid %d doesn't have camera permissions", __FUNCTION__, callingPid);
+ return STATUS_ERROR(ERROR_PERMISSION_DENIED,
+ "android.permission.CAMERA needed to call"
+ "isConcurrentSessionConfigurationSupported");
+ }
+
+ status_t res =
+ mCameraProviderManager->isConcurrentSessionConfigurationSupported(
+ cameraIdsAndSessionConfigurations, isSupported);
+ if (res != OK) {
+ return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION, "Unable to query session configuration "
+ "support %s (%d)", strerror(-res), res);
+ }
+ return Status::ok();
+}
+
Status CameraService::addListener(const sp<ICameraServiceListener>& listener,
/*out*/
std::vector<hardware::CameraStatus> *cameraStatuses) {
@@ -1966,7 +2174,8 @@
{
Mutex::Autolock lock(mCameraStatesLock);
for (auto& i : mCameraStates) {
- cameraStatuses->emplace_back(i.first, mapToInterface(i.second->getStatus()));
+ cameraStatuses->emplace_back(i.first,
+ mapToInterface(i.second->getStatus()), i.second->getUnavailablePhysicalIds());
}
}
// Remove the camera statuses that should be hidden from the client, we do
@@ -2093,6 +2302,7 @@
case CAMERA_DEVICE_API_VERSION_3_3:
case CAMERA_DEVICE_API_VERSION_3_4:
case CAMERA_DEVICE_API_VERSION_3_5:
+ case CAMERA_DEVICE_API_VERSION_3_6:
ALOGV("%s: Camera id %s uses HAL3.2 or newer, supports api1/api2 directly",
__FUNCTION__, id.string());
*isSupported = true;
@@ -2298,6 +2508,13 @@
clientPackage, clientPid));
}
+void CameraService::logDisconnectedOffline(const char* cameraId, int clientPid,
+ const char* clientPackage) {
+ // Log the clients evicted
+ logEvent(String8::format("DISCONNECT offline device %s client for package %s (PID %d)",
+ cameraId, clientPackage, clientPid));
+}
+
void CameraService::logConnected(const char* cameraId, int clientPid,
const char* clientPackage) {
// Log the clients evicted
@@ -2305,6 +2522,13 @@
clientPackage, clientPid));
}
+void CameraService::logConnectedOffline(const char* cameraId, int clientPid,
+ const char* clientPackage) {
+ // Log the clients evicted
+ logEvent(String8::format("CONNECT offline device %s client for package %s (PID %d)", cameraId,
+ clientPackage, clientPid));
+}
+
void CameraService::logRejected(const char* cameraId, int clientPid,
const char* clientPackage, const char* reason) {
// Log the client rejected
@@ -2742,6 +2966,7 @@
if (mAppOpsManager == nullptr) {
return;
}
+ // TODO : add offline camera session case
if (op != AppOpsManager::OP_CAMERA) {
ALOGW("Unexpected app ops notification received: %d", op);
return;
@@ -3032,10 +3257,9 @@
if (mRegistered) {
return;
}
- SensorPrivacyManager spm;
- spm.addSensorPrivacyListener(this);
- mSensorPrivacyEnabled = spm.isSensorPrivacyEnabled();
- status_t res = spm.linkToDeath(this);
+ mSpm.addSensorPrivacyListener(this);
+ mSensorPrivacyEnabled = mSpm.isSensorPrivacyEnabled();
+ status_t res = mSpm.linkToDeath(this);
if (res == OK) {
mRegistered = true;
ALOGV("SensorPrivacyPolicy: Registered with SensorPrivacyManager");
@@ -3044,9 +3268,8 @@
void CameraService::SensorPrivacyPolicy::unregisterSelf() {
Mutex::Autolock _l(mSensorPrivacyLock);
- SensorPrivacyManager spm;
- spm.removeSensorPrivacyListener(this);
- spm.unlinkToDeath(this);
+ mSpm.removeSensorPrivacyListener(this);
+ mSpm.unlinkToDeath(this);
mRegistered = false;
ALOGV("SensorPrivacyPolicy: Unregistered with SensorPrivacyManager");
}
@@ -3093,6 +3316,12 @@
return mStatus;
}
+std::vector<String8> CameraService::CameraState::getUnavailablePhysicalIds() const {
+ Mutex::Autolock lock(mStatusLock);
+ std::vector<String8> res(mUnavailablePhysicalIds.begin(), mUnavailablePhysicalIds.end());
+ return res;
+}
+
CameraParameters CameraService::CameraState::getShimParams() const {
return mShimParams;
}
@@ -3117,6 +3346,18 @@
return mSystemCameraKind;
}
+bool CameraService::CameraState::addUnavailablePhysicalId(const String8& physicalId) {
+ Mutex::Autolock lock(mStatusLock);
+ auto result = mUnavailablePhysicalIds.insert(physicalId);
+ return result.second;
+}
+
+bool CameraService::CameraState::removeUnavailablePhysicalId(const String8& physicalId) {
+ Mutex::Autolock lock(mStatusLock);
+ auto count = mUnavailablePhysicalIds.erase(physicalId);
+ return count > 0;
+}
+
// ----------------------------------------------------------------------------
// ClientEventListener
// ----------------------------------------------------------------------------
@@ -3474,7 +3715,7 @@
return;
}
// Update the status for this camera state, then send the onStatusChangedCallbacks to each
- // of the listeners with both the mStatusStatus and mStatusListenerLock held
+ // of the listeners with both the mStatusLock and mStatusListenerLock held
state->updateStatus(status, cameraId, rejectSourceStates, [this, &deviceKind, &supportsHAL3]
(const String8& cameraId, StatusInternal status) {
@@ -3496,6 +3737,8 @@
Mutex::Autolock lock(mStatusListenerLock);
+ notifyPhysicalCameraStatusLocked(mapToInterface(status), cameraId);
+
for (auto& listener : mListenerList) {
bool isVendorListener = listener->isVendorListener();
if (shouldSkipStatusUpdates(deviceKind, isVendorListener,
@@ -3589,6 +3832,28 @@
return OK;
}
+void CameraService::notifyPhysicalCameraStatusLocked(int32_t status, const String8& cameraId) {
+ Mutex::Autolock lock(mCameraStatesLock);
+ for (const auto& state : mCameraStates) {
+ std::vector<std::string> physicalCameraIds;
+ if (!mCameraProviderManager->isLogicalCamera(state.first.c_str(), &physicalCameraIds)) {
+ // This is not a logical multi-camera.
+ continue;
+ }
+ if (std::find(physicalCameraIds.begin(), physicalCameraIds.end(), cameraId.c_str())
+ == physicalCameraIds.end()) {
+ // cameraId is not a physical camera of this logical multi-camera.
+ continue;
+ }
+
+ String16 id16(state.first), physicalId16(cameraId);
+ for (auto& listener : mListenerList) {
+ listener->getListener()->onPhysicalCameraStatusChanged(status,
+ id16, physicalId16);
+ }
+ }
+}
+
void CameraService::blockClientsForUid(uid_t uid) {
const auto clients = mActiveClientManager.getAll();
for (auto& current : clients) {
@@ -3627,6 +3892,10 @@
return handleResetUidState(args, err);
} else if (args.size() >= 2 && args[0] == String16("get-uid-state")) {
return handleGetUidState(args, out, err);
+ } else if (args.size() >= 2 && args[0] == String16("set-rotate-and-crop")) {
+ return handleSetRotateAndCrop(args);
+ } else if (args.size() >= 1 && args[0] == String16("get-rotate-and-crop")) {
+ return handleGetRotateAndCrop(out);
} else if (args.size() == 1 && args[0] == String16("help")) {
printHelp(out);
return NO_ERROR;
@@ -3697,11 +3966,43 @@
}
}
+status_t CameraService::handleSetRotateAndCrop(const Vector<String16>& args) {
+ int rotateValue = atoi(String8(args[1]));
+ if (rotateValue < ANDROID_SCALER_ROTATE_AND_CROP_NONE ||
+ rotateValue > ANDROID_SCALER_ROTATE_AND_CROP_AUTO) return BAD_VALUE;
+ Mutex::Autolock lock(mServiceLock);
+
+ mOverrideRotateAndCropMode = rotateValue;
+
+ if (rotateValue == ANDROID_SCALER_ROTATE_AND_CROP_AUTO) return OK;
+
+ const auto clients = mActiveClientManager.getAll();
+ for (auto& current : clients) {
+ if (current != nullptr) {
+ const auto basicClient = current->getValue();
+ if (basicClient.get() != nullptr) {
+ basicClient->setRotateAndCropOverride(rotateValue);
+ }
+ }
+ }
+
+ return OK;
+}
+
+status_t CameraService::handleGetRotateAndCrop(int out) {
+ Mutex::Autolock lock(mServiceLock);
+
+ return dprintf(out, "rotateAndCrop override: %d\n", mOverrideRotateAndCropMode);
+}
+
status_t CameraService::printHelp(int out) {
return dprintf(out, "Camera service commands:\n"
" get-uid-state <PACKAGE> [--user USER_ID] gets the uid state\n"
" set-uid-state <PACKAGE> <active|idle> [--user USER_ID] overrides the uid state\n"
" reset-uid-state <PACKAGE> [--user USER_ID] clears the uid state override\n"
+ " set-rotate-and-crop <ROTATION> overrides the rotate-and-crop value for AUTO backcompat\n"
+ " Valid values 0=0 deg, 1=90 deg, 2=180 deg, 3=270 deg, 4=No override\n"
+ " get-rotate-and-crop returns the current override rotate-and-crop value\n"
" help print this message\n");
}
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 1f40fc3..265a572 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -31,12 +31,14 @@
#include <binder/IAppOpsCallback.h>
#include <binder/IUidObserver.h>
#include <hardware/camera.h>
+#include <sensorprivacy/SensorPrivacyManager.h>
#include <android/hardware/camera/common/1.0/types.h>
#include <camera/VendorTagDescriptor.h>
#include <camera/CaptureResult.h>
#include <camera/CameraParameters.h>
+#include <camera/camera2/ConcurrentCamera.h>
#include "CameraFlashlight.h"
@@ -68,9 +70,11 @@
{
friend class BinderService<CameraService>;
friend class CameraClient;
+ friend class CameraOfflineSessionClient;
public:
class Client;
class BasicClient;
+ class OfflineClient;
// The effective API level. The Camera2 API running in LEGACY mode counts as API_1.
enum apiLevel {
@@ -101,6 +105,9 @@
virtual void onDeviceStatusChanged(const String8 &cameraId,
hardware::camera::common::V1_0::CameraDeviceStatus newHalStatus) override;
+ virtual void onDeviceStatusChanged(const String8 &cameraId,
+ const String8 &physicalCameraId,
+ hardware::camera::common::V1_0::CameraDeviceStatus newHalStatus) override;
virtual void onTorchStatusChanged(const String8& cameraId,
hardware::camera::common::V1_0::TorchModeStatus newStatus) override;
virtual void onNewProviderRegistered() override;
@@ -145,6 +152,14 @@
virtual binder::Status removeListener(
const sp<hardware::ICameraServiceListener>& listener);
+ virtual binder::Status getConcurrentStreamingCameraIds(
+ /*out*/
+ std::vector<hardware::camera2::utils::ConcurrentCameraIdCombination>* concurrentCameraIds);
+
+ virtual binder::Status isConcurrentSessionConfigurationSupported(
+ const std::vector<hardware::camera2::utils::CameraIdAndSessionConfiguration>& sessions,
+ /*out*/bool* supported);
+
virtual binder::Status getLegacyParameters(
int32_t cameraId,
/*out*/
@@ -184,6 +199,9 @@
// Monitored UIDs availability notification
void notifyMonitoredUids();
+ // Register an offline client for a given active camera id
+ status_t addOfflineClient(String8 cameraId, sp<BasicClient> offlineClient);
+
/////////////////////////////////////////////////////////////////////
// Client functionality
@@ -272,6 +290,10 @@
virtual int32_t getAudioRestriction() const;
static bool isValidAudioRestriction(int32_t mode);
+
+ // Override rotate-and-crop AUTO behavior
+ virtual status_t setRotateAndCropOverride(uint8_t rotateAndCrop) = 0;
+
protected:
BasicClient(const sp<CameraService>& cameraService,
const sp<IBinder>& remoteCallback,
@@ -309,10 +331,9 @@
sp<IBinder> mRemoteBinder; // immutable after constructor
// permissions management
- status_t startCameraOps();
- status_t finishCameraOps();
+ virtual status_t startCameraOps();
+ virtual status_t finishCameraOps();
- private:
std::unique_ptr<AppOpsManager> mAppOpsManager = nullptr;
class OpsCallback : public BnAppOpsCallback {
@@ -552,11 +573,24 @@
*/
SystemCameraKind getSystemCameraKind() const;
+ /**
+ * Add/Remove the unavailable physical camera ID.
+ */
+ bool addUnavailablePhysicalId(const String8& physicalId);
+ bool removeUnavailablePhysicalId(const String8& physicalId);
+
+ /**
+ * Return the unavailable physical ids for this device.
+ *
+ * This method acquires mStatusLock.
+ */
+ std::vector<String8> getUnavailablePhysicalIds() const;
private:
const String8 mId;
StatusInternal mStatus; // protected by mStatusLock
const int mCost;
std::set<String8> mConflicting;
+ std::set<String8> mUnavailablePhysicalIds;
mutable Mutex mStatusLock;
CameraParameters mShimParams;
const SystemCameraKind mSystemCameraKind;
@@ -623,6 +657,7 @@
virtual void binderDied(const wp<IBinder> &who);
private:
+ SensorPrivacyManager mSpm;
wp<CameraService> mService;
Mutex mSensorPrivacyLock;
bool mSensorPrivacyEnabled;
@@ -790,6 +825,17 @@
void logDisconnected(const char* cameraId, int clientPid, const char* clientPackage);
/**
+ * Add an event log message that a client has been disconnected from offline device.
+ */
+ void logDisconnectedOffline(const char* cameraId, int clientPid, const char* clientPackage);
+
+ /**
+ * Add an event log message that an offline client has been connected.
+ */
+ void logConnectedOffline(const char* cameraId, int clientPid,
+ const char* clientPackage);
+
+ /**
* Add an event log message that a client has been connected.
*/
void logConnected(const char* cameraId, int clientPid, const char* clientPackage);
@@ -947,6 +993,9 @@
status_t setTorchStatusLocked(const String8 &cameraId,
hardware::camera::common::V1_0::TorchModeStatus status);
+ // notify physical camera status when the physical camera is public.
+ void notifyPhysicalCameraStatusLocked(int32_t status, const String8& cameraId);
+
// IBinder::DeathRecipient implementation
virtual void binderDied(const wp<IBinder> &who);
@@ -980,6 +1029,12 @@
// Gets the UID state
status_t handleGetUidState(const Vector<String16>& args, int out, int err);
+ // Set the rotate-and-crop AUTO override behavior
+ status_t handleSetRotateAndCrop(const Vector<String16>& args);
+
+ // Get the rotate-and-crop AUTO override behavior
+ status_t handleGetRotateAndCrop(int out);
+
// Prints the shell command help
status_t printHelp(int out);
@@ -1013,12 +1068,21 @@
void broadcastTorchModeStatus(const String8& cameraId,
hardware::camera::common::V1_0::TorchModeStatus status);
+ void disconnectClient(const String8& id, sp<BasicClient> clientToDisconnect);
+
+ // Regular online and offline devices must not be in conflict at camera service layer.
+ // Use separate keys for offline devices.
+ static const String8 kOfflineDevice;
+
// TODO: right now each BasicClient holds one AppOpsManager instance.
// We can refactor the code so all of clients share this instance
AppOpsManager mAppOps;
// Aggreated audio restriction mode for all camera clients
int32_t mAudioRestriction;
+
+ // Current override rotate-and-crop mode
+ uint8_t mOverrideRotateAndCropMode = ANDROID_SCALER_ROTATE_AND_CROP_AUTO;
};
} // namespace android
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 5dbbc0b..1d62a74 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -2271,6 +2271,13 @@
return INVALID_OPERATION;
}
+status_t Camera2Client::setRotateAndCropOverride(uint8_t rotateAndCrop) {
+ if (rotateAndCrop > ANDROID_SCALER_ROTATE_AND_CROP_AUTO) return BAD_VALUE;
+
+ return mDevice->setRotateAndCropAutoBehavior(
+ static_cast<camera_metadata_enum_android_scaler_rotate_and_crop_t>(rotateAndCrop));
+}
+
status_t Camera2Client::waitUntilCurrentRequestIdLocked() {
int32_t activeRequestId = mStreamingProcessor->getActiveRequestId();
if (activeRequestId != 0) {
diff --git a/services/camera/libcameraservice/api1/Camera2Client.h b/services/camera/libcameraservice/api1/Camera2Client.h
index 8034ab4..3144e0e 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.h
+++ b/services/camera/libcameraservice/api1/Camera2Client.h
@@ -85,6 +85,7 @@
virtual status_t setVideoTarget(const sp<IGraphicBufferProducer>& bufferProducer);
virtual status_t setAudioRestriction(int mode);
virtual int32_t getGlobalAudioRestriction();
+ virtual status_t setRotateAndCropOverride(uint8_t rotateAndCrop);
/**
* Interface used by CameraService
diff --git a/services/camera/libcameraservice/api1/CameraClient.cpp b/services/camera/libcameraservice/api1/CameraClient.cpp
index 83da880..892996c 100644
--- a/services/camera/libcameraservice/api1/CameraClient.cpp
+++ b/services/camera/libcameraservice/api1/CameraClient.cpp
@@ -1200,4 +1200,9 @@
return BasicClient::getServiceAudioRestriction();
}
+// API1->Device1 does not support this feature
+status_t CameraClient::setRotateAndCropOverride(uint8_t /*rotateAndCrop*/) {
+ return OK;
+}
+
}; // namespace android
diff --git a/services/camera/libcameraservice/api1/CameraClient.h b/services/camera/libcameraservice/api1/CameraClient.h
index 501ad18..a7eb960 100644
--- a/services/camera/libcameraservice/api1/CameraClient.h
+++ b/services/camera/libcameraservice/api1/CameraClient.h
@@ -62,6 +62,8 @@
virtual status_t setAudioRestriction(int mode);
virtual int32_t getGlobalAudioRestriction();
+ virtual status_t setRotateAndCropOverride(uint8_t override);
+
// Interface used by CameraService
CameraClient(const sp<CameraService>& cameraService,
const sp<hardware::ICameraClient>& cameraClient,
diff --git a/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp b/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp
index 63e293a..2daacd1 100644
--- a/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp
@@ -66,7 +66,7 @@
}
bool FrameProcessor::processSingleFrame(CaptureResult &frame,
- const sp<CameraDeviceBase> &device) {
+ const sp<FrameProducer> &device) {
sp<Camera2Client> client = mClient.promote();
if (!client.get()) {
diff --git a/services/camera/libcameraservice/api1/client2/FrameProcessor.h b/services/camera/libcameraservice/api1/client2/FrameProcessor.h
index 142b8cd..bb985f6 100644
--- a/services/camera/libcameraservice/api1/client2/FrameProcessor.h
+++ b/services/camera/libcameraservice/api1/client2/FrameProcessor.h
@@ -24,6 +24,7 @@
#include <utils/List.h>
#include <camera/CameraMetadata.h>
+#include "common/CameraDeviceBase.h"
#include "common/FrameProcessorBase.h"
struct camera_frame_metadata;
@@ -54,7 +55,7 @@
void processNewFrames(const sp<Camera2Client> &client);
virtual bool processSingleFrame(CaptureResult &frame,
- const sp<CameraDeviceBase> &device);
+ const sp<FrameProducer> &device);
status_t processFaceDetect(const CameraMetadata &frame,
const sp<Camera2Client> &client);
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index 18addb5..20333d1 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -2454,12 +2454,9 @@
camera_metadata_ro_entry_t availableFocalLengths =
staticInfo(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS, 0, 0, /*required*/false);
- if (!availableFocalLengths.count && !fastInfo.isExternalCamera) return NO_INIT;
// Find focal length in PREVIEW template to use as default focal length.
- if (fastInfo.isExternalCamera) {
- fastInfo.defaultFocalLength = -1.0;
- } else {
+ if (availableFocalLengths.count) {
// Find smallest (widest-angle) focal length to use as basis of still
// picture FOV reporting.
fastInfo.defaultFocalLength = availableFocalLengths.data.f[0];
@@ -2481,6 +2478,10 @@
if (entry.count != 0) {
fastInfo.defaultFocalLength = entry.data.f[0];
}
+ } else if (fastInfo.isExternalCamera) {
+ fastInfo.defaultFocalLength = -1.0;
+ } else {
+ return NO_INIT;
}
return OK;
}
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 27bebde..e35b436 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -117,8 +117,8 @@
threadName = String8::format("CDU-%s-FrameProc", mCameraIdStr.string());
mFrameProcessor->run(threadName.string());
- mFrameProcessor->registerListener(FRAME_PROCESSOR_LISTENER_MIN_ID,
- FRAME_PROCESSOR_LISTENER_MAX_ID,
+ mFrameProcessor->registerListener(camera2::FrameProcessorBase::FRAME_PROCESSOR_LISTENER_MIN_ID,
+ camera2::FrameProcessorBase::FRAME_PROCESSOR_LISTENER_MAX_ID,
/*listener*/this,
/*sendPartials*/true);
@@ -470,7 +470,8 @@
}
binder::Status CameraDeviceClient::endConfigure(int operatingMode,
- const hardware::camera2::impl::CameraMetadataNative& sessionParams) {
+ const hardware::camera2::impl::CameraMetadataNative& sessionParams,
+ std::vector<int>* offlineStreamIds /*out*/) {
ATRACE_CALL();
ALOGV("%s: ending configure (%d input stream, %zu output surfaces)",
__FUNCTION__, mInputStream.configured ? 1 : 0,
@@ -479,13 +480,19 @@
binder::Status res;
if (!(res = checkPidStatus(__FUNCTION__)).isOk()) return res;
+ if (offlineStreamIds == nullptr) {
+ String8 msg = String8::format("Invalid offline stream ids");
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+ }
+
Mutex::Autolock icl(mBinderSerializationLock);
if (!mDevice.get()) {
return STATUS_ERROR(CameraService::ERROR_DISCONNECTED, "Camera device no longer alive");
}
- res = checkOperatingModeLocked(operatingMode);
+ res = checkOperatingMode(operatingMode, mDevice->info(), mCameraIdStr);
if (!res.isOk()) {
return res;
}
@@ -502,23 +509,49 @@
ALOGE("%s: %s", __FUNCTION__, msg.string());
res = STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());
} else {
+ offlineStreamIds->clear();
+ mDevice->getOfflineStreamIds(offlineStreamIds);
+
for (size_t i = 0; i < mCompositeStreamMap.size(); ++i) {
err = mCompositeStreamMap.valueAt(i)->configureStream();
- if (err != OK ) {
+ if (err != OK) {
String8 msg = String8::format("Camera %s: Error configuring composite "
"streams: %s (%d)", mCameraIdStr.string(), strerror(-err), err);
ALOGE("%s: %s", __FUNCTION__, msg.string());
res = STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());
break;
}
+
+ // Composite streams can only support offline mode in case all individual internal
+ // streams are also supported.
+ std::vector<int> internalStreams;
+ mCompositeStreamMap.valueAt(i)->insertCompositeStreamIds(&internalStreams);
+ offlineStreamIds->erase(
+ std::remove_if(offlineStreamIds->begin(), offlineStreamIds->end(),
+ [&internalStreams] (int streamId) {
+ auto it = std::find(internalStreams.begin(), internalStreams.end(),
+ streamId);
+ if (it != internalStreams.end()) {
+ internalStreams.erase(it);
+ return true;
+ }
+
+ return false;}), offlineStreamIds->end());
+ if (internalStreams.empty()) {
+ offlineStreamIds->push_back(mCompositeStreamMap.valueAt(i)->getStreamId());
+ }
+ }
+
+ for (const auto& offlineStreamId : *offlineStreamIds) {
+ mStreamInfoMap[offlineStreamId].supportsOffline = true;
}
}
return res;
}
-binder::Status CameraDeviceClient::checkSurfaceTypeLocked(size_t numBufferProducers,
- bool deferredConsumer, int surfaceType) const {
+binder::Status CameraDeviceClient::checkSurfaceType(size_t numBufferProducers,
+ bool deferredConsumer, int surfaceType) {
if (numBufferProducers > MAX_SURFACES_PER_STREAM) {
ALOGE("%s: GraphicBufferProducer count %zu for stream exceeds limit of %d",
__FUNCTION__, numBufferProducers, MAX_SURFACES_PER_STREAM);
@@ -539,28 +572,27 @@
return binder::Status::ok();
}
-binder::Status CameraDeviceClient::checkPhysicalCameraIdLocked(String8 physicalCameraId) {
- if (physicalCameraId.size() > 0) {
- std::vector<std::string> physicalCameraIds;
- bool logicalCamera =
- mProviderManager->isLogicalCamera(mCameraIdStr.string(), &physicalCameraIds);
- if (!logicalCamera ||
- std::find(physicalCameraIds.begin(), physicalCameraIds.end(),
- physicalCameraId.string()) == physicalCameraIds.end()) {
- String8 msg = String8::format("Camera %s: Camera doesn't support physicalCameraId %s.",
- mCameraIdStr.string(), physicalCameraId.string());
- ALOGE("%s: %s", __FUNCTION__, msg.string());
- return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
- }
+binder::Status CameraDeviceClient::checkPhysicalCameraId(
+ const std::vector<std::string> &physicalCameraIds, const String8 &physicalCameraId,
+ const String8 &logicalCameraId) {
+ if (physicalCameraId.size() == 0) {
+ return binder::Status::ok();
}
-
+ if (std::find(physicalCameraIds.begin(), physicalCameraIds.end(),
+ physicalCameraId.string()) == physicalCameraIds.end()) {
+ String8 msg = String8::format("Camera %s: Camera doesn't support physicalCameraId %s.",
+ logicalCameraId.string(), physicalCameraId.string());
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+ }
return binder::Status::ok();
}
-binder::Status CameraDeviceClient::checkOperatingModeLocked(int operatingMode) const {
+binder::Status CameraDeviceClient::checkOperatingMode(int operatingMode,
+ const CameraMetadata &staticInfo, const String8 &cameraId) {
if (operatingMode < 0) {
String8 msg = String8::format(
- "Camera %s: Invalid operating mode %d requested", mCameraIdStr.string(), operatingMode);
+ "Camera %s: Invalid operating mode %d requested", cameraId.string(), operatingMode);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
msg.string());
@@ -568,8 +600,7 @@
bool isConstrainedHighSpeed = (operatingMode == ICameraDeviceUser::CONSTRAINED_HIGH_SPEED_MODE);
if (isConstrainedHighSpeed) {
- CameraMetadata staticInfo = mDevice->info();
- camera_metadata_entry_t entry = staticInfo.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
+ camera_metadata_ro_entry_t entry = staticInfo.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
bool isConstrainedHighSpeedSupported = false;
for(size_t i = 0; i < entry.count; ++i) {
uint8_t capability = entry.data.u8[i];
@@ -581,7 +612,7 @@
if (!isConstrainedHighSpeedSupported) {
String8 msg = String8::format(
"Camera %s: Try to create a constrained high speed configuration on a device"
- " that doesn't support it.", mCameraIdStr.string());
+ " that doesn't support it.", cameraId.string());
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
msg.string());
@@ -612,39 +643,31 @@
stream->bufferSize = 0;
}
-binder::Status CameraDeviceClient::isSessionConfigurationSupported(
- const SessionConfiguration& sessionConfiguration, bool *status /*out*/) {
- ATRACE_CALL();
-
- binder::Status res;
- if (!(res = checkPidStatus(__FUNCTION__)).isOk()) return res;
-
- Mutex::Autolock icl(mBinderSerializationLock);
-
- if (!mDevice.get()) {
- return STATUS_ERROR(CameraService::ERROR_DISCONNECTED, "Camera device no longer alive");
- }
-
+binder::Status
+CameraDeviceClient::convertToHALStreamCombination(const SessionConfiguration& sessionConfiguration,
+ const String8 &logicalCameraId, const CameraMetadata &deviceInfo,
+ metadataGetter getMetadata, const std::vector<std::string> &physicalCameraIds,
+ hardware::camera::device::V3_4::StreamConfiguration &streamConfiguration,
+ bool *unsupported) {
auto operatingMode = sessionConfiguration.getOperatingMode();
- res = checkOperatingModeLocked(operatingMode);
+ binder::Status res = checkOperatingMode(operatingMode, deviceInfo, logicalCameraId);
if (!res.isOk()) {
return res;
}
- if (status == nullptr) {
- String8 msg = String8::format( "Camera %s: Invalid status!", mCameraIdStr.string());
+ if (unsupported == nullptr) {
+ String8 msg("unsupported nullptr");
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
}
-
- hardware::camera::device::V3_4::StreamConfiguration streamConfiguration;
+ *unsupported = false;
auto ret = Camera3Device::mapToStreamConfigurationMode(
static_cast<camera3_stream_configuration_mode_t> (operatingMode),
/*out*/ &streamConfiguration.operationMode);
if (ret != OK) {
String8 msg = String8::format(
- "Camera %s: Failed mapping operating mode %d requested: %s (%d)", mCameraIdStr.string(),
- operatingMode, strerror(-ret), ret);
+ "Camera %s: Failed mapping operating mode %d requested: %s (%d)",
+ logicalCameraId.string(), operatingMode, strerror(-ret), ret);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
msg.string());
@@ -678,12 +701,12 @@
bool isStreamInfoValid = false;
OutputStreamInfo streamInfo;
- res = checkSurfaceTypeLocked(numBufferProducers, deferredConsumer, it.getSurfaceType());
+ res = checkSurfaceType(numBufferProducers, deferredConsumer, it.getSurfaceType());
if (!res.isOk()) {
return res;
}
-
- res = checkPhysicalCameraIdLocked(physicalCameraId);
+ res = checkPhysicalCameraId(physicalCameraIds, physicalCameraId,
+ logicalCameraId);
if (!res.isOk()) {
return res;
}
@@ -709,8 +732,10 @@
for (auto& bufferProducer : bufferProducers) {
sp<Surface> surface;
+ const CameraMetadata &physicalDeviceInfo = getMetadata(physicalCameraId);
res = createSurfaceFromGbp(streamInfo, isStreamInfoValid, surface, bufferProducer,
- physicalCameraId);
+ logicalCameraId,
+ physicalCameraId.size() > 0 ? physicalDeviceInfo : deviceInfo );
if (!res.isOk())
return res;
@@ -726,15 +751,15 @@
std::vector<OutputStreamInfo> compositeStreams;
if (isDepthCompositeStream) {
ret = camera3::DepthCompositeStream::getCompositeStreamInfo(streamInfo,
- mDevice->info(), &compositeStreams);
+ deviceInfo, &compositeStreams);
} else {
ret = camera3::HeicCompositeStream::getCompositeStreamInfo(streamInfo,
- mDevice->info(), &compositeStreams);
+ deviceInfo, &compositeStreams);
}
if (ret != OK) {
String8 msg = String8::format(
"Camera %s: Failed adding composite streams: %s (%d)",
- mCameraIdStr.string(), strerror(-ret), ret);
+ logicalCameraId.string(), strerror(-ret), ret);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
}
@@ -742,7 +767,7 @@
if (compositeStreams.size() == 0) {
// No internal streams means composite stream not
// supported.
- *status = false;
+ *unsupported = true;
return binder::Status::ok();
} else if (compositeStreams.size() > 1) {
streamCount += compositeStreams.size() - 1;
@@ -763,6 +788,49 @@
}
}
}
+ return binder::Status::ok();
+}
+
+binder::Status CameraDeviceClient::isSessionConfigurationSupported(
+ const SessionConfiguration& sessionConfiguration, bool *status /*out*/) {
+ ATRACE_CALL();
+
+ binder::Status res;
+ status_t ret = OK;
+ if (!(res = checkPidStatus(__FUNCTION__)).isOk()) return res;
+
+ Mutex::Autolock icl(mBinderSerializationLock);
+
+ if (!mDevice.get()) {
+ return STATUS_ERROR(CameraService::ERROR_DISCONNECTED, "Camera device no longer alive");
+ }
+
+ auto operatingMode = sessionConfiguration.getOperatingMode();
+ res = checkOperatingMode(operatingMode, mDevice->info(), mCameraIdStr);
+ if (!res.isOk()) {
+ return res;
+ }
+
+ if (status == nullptr) {
+ String8 msg = String8::format( "Camera %s: Invalid status!", mCameraIdStr.string());
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+ }
+ hardware::camera::device::V3_4::StreamConfiguration streamConfiguration;
+ bool earlyExit = false;
+ metadataGetter getMetadata = [this](const String8 &id) {return mDevice->infoPhysical(id);};
+ std::vector<std::string> physicalCameraIds;
+ mProviderManager->isLogicalCamera(mCameraIdStr.string(), &physicalCameraIds);
+ res = convertToHALStreamCombination(sessionConfiguration, mCameraIdStr,
+ mDevice->info(), getMetadata, physicalCameraIds, streamConfiguration, &earlyExit);
+ if (!res.isOk()) {
+ return res;
+ }
+
+ if (earlyExit) {
+ *status = false;
+ return binder::Status::ok();
+ }
*status = false;
ret = mProviderManager->isSessionConfigurationSupported(mCameraIdStr.string(),
@@ -902,7 +970,7 @@
String8 physicalCameraId = String8(outputConfiguration.getPhysicalCameraId());
bool deferredConsumerOnly = deferredConsumer && numBufferProducers == 0;
- res = checkSurfaceTypeLocked(numBufferProducers, deferredConsumer,
+ res = checkSurfaceType(numBufferProducers, deferredConsumer,
outputConfiguration.getSurfaceType());
if (!res.isOk()) {
return res;
@@ -911,8 +979,9 @@
if (!mDevice.get()) {
return STATUS_ERROR(CameraService::ERROR_DISCONNECTED, "Camera device no longer alive");
}
-
- res = checkPhysicalCameraIdLocked(physicalCameraId);
+ std::vector<std::string> physicalCameraIds;
+ mProviderManager->isLogicalCamera(mCameraIdStr.string(), &physicalCameraIds);
+ res = checkPhysicalCameraId(physicalCameraIds, physicalCameraId, mCameraIdStr);
if (!res.isOk()) {
return res;
}
@@ -941,7 +1010,7 @@
sp<Surface> surface;
res = createSurfaceFromGbp(streamInfo, isStreamInfoValid, surface, bufferProducer,
- physicalCameraId);
+ mCameraIdStr, mDevice->infoPhysical(physicalCameraId));
if (!res.isOk())
return res;
@@ -1245,7 +1314,7 @@
OutputStreamInfo outInfo;
sp<Surface> surface;
res = createSurfaceFromGbp(outInfo, /*isStreamInfoValid*/ false, surface,
- newOutputsMap.valueAt(i), physicalCameraId);
+ newOutputsMap.valueAt(i), mCameraIdStr, mDevice->infoPhysical(physicalCameraId));
if (!res.isOk())
return res;
@@ -1325,11 +1394,11 @@
binder::Status CameraDeviceClient::createSurfaceFromGbp(
OutputStreamInfo& streamInfo, bool isStreamInfoValid,
sp<Surface>& surface, const sp<IGraphicBufferProducer>& gbp,
- const String8& physicalId) {
+ const String8 &cameraId, const CameraMetadata &physicalCameraMetadata) {
// bufferProducer must be non-null
if (gbp == nullptr) {
- String8 msg = String8::format("Camera %s: Surface is NULL", mCameraIdStr.string());
+ String8 msg = String8::format("Camera %s: Surface is NULL", cameraId.string());
ALOGW("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
}
@@ -1341,13 +1410,13 @@
status_t err;
if ((err = gbp->getConsumerUsage(&consumerUsage)) != OK) {
String8 msg = String8::format("Camera %s: Failed to query Surface consumer usage: %s (%d)",
- mCameraIdStr.string(), strerror(-err), err);
+ cameraId.string(), strerror(-err), err);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());
}
if (consumerUsage & GraphicBuffer::USAGE_HW_TEXTURE) {
ALOGW("%s: Camera %s with consumer usage flag: %" PRIu64 ": Forcing asynchronous mode for stream",
- __FUNCTION__, mCameraIdStr.string(), consumerUsage);
+ __FUNCTION__, cameraId.string(), consumerUsage);
useAsync = true;
}
@@ -1366,26 +1435,26 @@
android_dataspace dataSpace;
if ((err = anw->query(anw, NATIVE_WINDOW_WIDTH, &width)) != OK) {
String8 msg = String8::format("Camera %s: Failed to query Surface width: %s (%d)",
- mCameraIdStr.string(), strerror(-err), err);
+ cameraId.string(), strerror(-err), err);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());
}
if ((err = anw->query(anw, NATIVE_WINDOW_HEIGHT, &height)) != OK) {
String8 msg = String8::format("Camera %s: Failed to query Surface height: %s (%d)",
- mCameraIdStr.string(), strerror(-err), err);
+ cameraId.string(), strerror(-err), err);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());
}
if ((err = anw->query(anw, NATIVE_WINDOW_FORMAT, &format)) != OK) {
String8 msg = String8::format("Camera %s: Failed to query Surface format: %s (%d)",
- mCameraIdStr.string(), strerror(-err), err);
+ cameraId.string(), strerror(-err), err);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());
}
if ((err = anw->query(anw, NATIVE_WINDOW_DEFAULT_DATASPACE,
reinterpret_cast<int*>(&dataSpace))) != OK) {
String8 msg = String8::format("Camera %s: Failed to query Surface dataspace: %s (%d)",
- mCameraIdStr.string(), strerror(-err), err);
+ cameraId.string(), strerror(-err), err);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());
}
@@ -1396,16 +1465,16 @@
((consumerUsage & GRALLOC_USAGE_HW_MASK) &&
((consumerUsage & GRALLOC_USAGE_SW_READ_MASK) == 0))) {
ALOGW("%s: Camera %s: Overriding format %#x to IMPLEMENTATION_DEFINED",
- __FUNCTION__, mCameraIdStr.string(), format);
+ __FUNCTION__, cameraId.string(), format);
format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
}
// Round dimensions to the nearest dimensions available for this format
if (flexibleConsumer && isPublicFormat(format) &&
!CameraDeviceClient::roundBufferDimensionNearest(width, height,
- format, dataSpace, mDevice->info(physicalId), /*out*/&width, /*out*/&height)) {
+ format, dataSpace, physicalCameraMetadata, /*out*/&width, /*out*/&height)) {
String8 msg = String8::format("Camera %s: No supported stream configurations with "
"format %#x defined, failed to create output stream",
- mCameraIdStr.string(), format);
+ cameraId.string(), format);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
}
@@ -1420,26 +1489,26 @@
}
if (width != streamInfo.width) {
String8 msg = String8::format("Camera %s:Surface width doesn't match: %d vs %d",
- mCameraIdStr.string(), width, streamInfo.width);
+ cameraId.string(), width, streamInfo.width);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
}
if (height != streamInfo.height) {
String8 msg = String8::format("Camera %s:Surface height doesn't match: %d vs %d",
- mCameraIdStr.string(), height, streamInfo.height);
+ cameraId.string(), height, streamInfo.height);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
}
if (format != streamInfo.format) {
String8 msg = String8::format("Camera %s:Surface format doesn't match: %d vs %d",
- mCameraIdStr.string(), format, streamInfo.format);
+ cameraId.string(), format, streamInfo.format);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
}
if (format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
if (dataSpace != streamInfo.dataSpace) {
String8 msg = String8::format("Camera %s:Surface dataSpace doesn't match: %d vs %d",
- mCameraIdStr.string(), dataSpace, streamInfo.dataSpace);
+ cameraId.string(), dataSpace, streamInfo.dataSpace);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
}
@@ -1448,7 +1517,7 @@
if (consumerUsage != streamInfo.consumerUsage) {
String8 msg = String8::format(
"Camera %s:Surface usage flag doesn't match %" PRIu64 " vs %" PRIu64 "",
- mCameraIdStr.string(), consumerUsage, streamInfo.consumerUsage);
+ cameraId.string(), consumerUsage, streamInfo.consumerUsage);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
}
@@ -1828,7 +1897,7 @@
sp<Surface> surface;
res = createSurfaceFromGbp(mStreamInfoMap[streamId], true /*isStreamInfoValid*/,
- surface, bufferProducer, physicalId);
+ surface, bufferProducer, mCameraIdStr, mDevice->infoPhysical(physicalId));
if (!res.isOk())
return res;
@@ -1901,6 +1970,131 @@
return binder::Status::ok();
}
+status_t CameraDeviceClient::setRotateAndCropOverride(uint8_t rotateAndCrop) {
+ if (rotateAndCrop > ANDROID_SCALER_ROTATE_AND_CROP_AUTO) return BAD_VALUE;
+
+ return mDevice->setRotateAndCropAutoBehavior(
+ static_cast<camera_metadata_enum_android_scaler_rotate_and_crop_t>(rotateAndCrop));
+}
+
+binder::Status CameraDeviceClient::switchToOffline(
+ const sp<hardware::camera2::ICameraDeviceCallbacks>& cameraCb,
+ const std::vector<int>& offlineOutputIds,
+ /*out*/
+ sp<hardware::camera2::ICameraOfflineSession>* session) {
+ ATRACE_CALL();
+
+ binder::Status res;
+ if (!(res = checkPidStatus(__FUNCTION__)).isOk()) return res;
+
+ Mutex::Autolock icl(mBinderSerializationLock);
+
+ if (!mDevice.get()) {
+ return STATUS_ERROR(CameraService::ERROR_DISCONNECTED, "Camera device no longer alive");
+ }
+
+ if (offlineOutputIds.empty()) {
+ String8 msg = String8::format("Offline surfaces must not be empty");
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+ }
+
+ if (session == nullptr) {
+ String8 msg = String8::format("Invalid offline session");
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+ }
+
+ std::vector<int32_t> offlineStreamIds;
+ offlineStreamIds.reserve(offlineOutputIds.size());
+ KeyedVector<sp<IBinder>, sp<CompositeStream>> offlineCompositeStreamMap;
+ for (const auto& streamId : offlineOutputIds) {
+ ssize_t index = mConfiguredOutputs.indexOfKey(streamId);
+ if (index == NAME_NOT_FOUND) {
+ String8 msg = String8::format("Offline surface with id: %d is not registered",
+ streamId);
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+ }
+
+ if (!mStreamInfoMap[streamId].supportsOffline) {
+ String8 msg = String8::format("Offline surface with id: %d doesn't support "
+ "offline mode", streamId);
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+ }
+
+ bool isCompositeStream = false;
+ for (const auto& gbp : mConfiguredOutputs[streamId].getGraphicBufferProducers()) {
+ sp<Surface> s = new Surface(gbp, false /*controlledByApp*/);
+ isCompositeStream = camera3::DepthCompositeStream::isDepthCompositeStream(s) |
+ camera3::HeicCompositeStream::isHeicCompositeStream(s);
+ if (isCompositeStream) {
+ auto compositeIdx = mCompositeStreamMap.indexOfKey(IInterface::asBinder(gbp));
+ if (compositeIdx == NAME_NOT_FOUND) {
+ ALOGE("%s: Unknown composite stream", __FUNCTION__);
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
+ "Unknown composite stream");
+ }
+
+ mCompositeStreamMap.valueAt(compositeIdx)->insertCompositeStreamIds(
+ &offlineStreamIds);
+ offlineCompositeStreamMap.add(mCompositeStreamMap.keyAt(compositeIdx),
+ mCompositeStreamMap.valueAt(compositeIdx));
+ break;
+ }
+ }
+
+ if (!isCompositeStream) {
+ offlineStreamIds.push_back(streamId);
+ }
+ }
+
+ sp<CameraOfflineSessionBase> offlineSession;
+ auto ret = mDevice->switchToOffline(offlineStreamIds, &offlineSession);
+ if (ret != OK) {
+ return STATUS_ERROR_FMT(CameraService::ERROR_ILLEGAL_ARGUMENT,
+ "Camera %s: Error switching to offline mode: %s (%d)",
+ mCameraIdStr.string(), strerror(ret), ret);
+ }
+
+ sp<CameraOfflineSessionClient> offlineClient;
+ if (offlineSession.get() != nullptr) {
+ offlineClient = new CameraOfflineSessionClient(sCameraService,
+ offlineSession, offlineCompositeStreamMap, cameraCb, mClientPackageName,
+ mClientFeatureId, mCameraIdStr, mCameraFacing, mClientPid, mClientUid, mServicePid);
+ ret = sCameraService->addOfflineClient(mCameraIdStr, offlineClient);
+ }
+
+ if (ret == OK) {
+ // A successful offline session switch must reset the current camera client
+ // and release any resources occupied by previously configured streams.
+ mStreamMap.clear();
+ mConfiguredOutputs.clear();
+ mDeferredStreams.clear();
+ mStreamInfoMap.clear();
+ mCompositeStreamMap.clear();
+ mInputStream = {false, 0, 0, 0, 0};
+ } else {
+ switch(ret) {
+ case BAD_VALUE:
+ return STATUS_ERROR_FMT(CameraService::ERROR_ILLEGAL_ARGUMENT,
+ "Illegal argument to HAL module for camera \"%s\"", mCameraIdStr.c_str());
+ case TIMED_OUT:
+ return STATUS_ERROR_FMT(CameraService::ERROR_CAMERA_IN_USE,
+ "Camera \"%s\" is already open", mCameraIdStr.c_str());
+ default:
+ return STATUS_ERROR_FMT(CameraService::ERROR_INVALID_OPERATION,
+ "Failed to initialize camera \"%s\": %s (%d)", mCameraIdStr.c_str(),
+ strerror(-ret), ret);
+ }
+ }
+
+ *session = offlineClient;
+
+ return binder::Status::ok();
+}
+
status_t CameraDeviceClient::dump(int fd, const Vector<String16>& args) {
return BasicClient::dump(fd, args);
}
@@ -2013,8 +2207,8 @@
ALOGV("Camera %s: Stopping processors", mCameraIdStr.string());
- mFrameProcessor->removeListener(FRAME_PROCESSOR_LISTENER_MIN_ID,
- FRAME_PROCESSOR_LISTENER_MAX_ID,
+ mFrameProcessor->removeListener(camera2::FrameProcessorBase::FRAME_PROCESSOR_LISTENER_MIN_ID,
+ camera2::FrameProcessorBase::FRAME_PROCESSOR_LISTENER_MAX_ID,
/*listener*/this);
mFrameProcessor->requestExit();
ALOGV("Camera %s: Waiting for threads", mCameraIdStr.string());
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index 7efc5ab..964c96a 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -23,6 +23,7 @@
#include <camera/camera2/SessionConfiguration.h>
#include <camera/camera2/SubmitInfo.h>
+#include "CameraOfflineSessionClient.h"
#include "CameraService.h"
#include "common/FrameProcessorBase.h"
#include "common/Camera2ClientBase.h"
@@ -33,6 +34,8 @@
namespace android {
+typedef std::function<CameraMetadata (const String8 &)> metadataGetter;
+
struct CameraDeviceClientBase :
public CameraService::BasicClient,
public hardware::camera2::BnCameraDeviceUser
@@ -91,7 +94,9 @@
virtual binder::Status beginConfigure() override;
virtual binder::Status endConfigure(int operatingMode,
- const hardware::camera2::impl::CameraMetadataNative& sessionParams) override;
+ const hardware::camera2::impl::CameraMetadataNative& sessionParams,
+ /*out*/
+ std::vector<int>* offlineStreamIds) override;
// Verify specific session configuration.
virtual binder::Status isSessionConfigurationSupported(
@@ -157,6 +162,12 @@
virtual binder::Status getGlobalAudioRestriction(/*out*/int32_t* outMode) override;
+ virtual binder::Status switchToOffline(
+ const sp<hardware::camera2::ICameraDeviceCallbacks>& cameraCb,
+ const std::vector<int>& offlineOutputIds,
+ /*out*/
+ sp<hardware::camera2::ICameraOfflineSession>* session) override;
+
/**
* Interface used by CameraService
*/
@@ -175,6 +186,8 @@
virtual status_t initialize(sp<CameraProviderManager> manager,
const String8& monitorTags) override;
+ virtual status_t setRotateAndCropOverride(uint8_t rotateAndCrop) override;
+
virtual status_t dump(int fd, const Vector<String16>& args);
virtual status_t dumpClient(int fd, const Vector<String16>& args);
@@ -191,6 +204,16 @@
virtual void notifyRequestQueueEmpty();
virtual void notifyRepeatingRequestError(long lastFrameNumber);
+ // utility function to convert AIDL SessionConfiguration to HIDL
+ // streamConfiguration. Also checks for sanity of SessionConfiguration and
+ // returns a non-ok binder::Status if the passed in session configuration
+ // isn't valid.
+ static binder::Status
+ convertToHALStreamCombination(const SessionConfiguration& sessionConfiguration,
+ const String8 &cameraId, const CameraMetadata &deviceInfo,
+ metadataGetter getMetadata, const std::vector<std::string> &physicalCameraIds,
+ hardware::camera::device::V3_4::StreamConfiguration &streamConfiguration,
+ bool *earlyExit);
/**
* Interface used by independent components of CameraDeviceClient.
*/
@@ -235,8 +258,6 @@
/** Preview callback related members */
sp<camera2::FrameProcessorBase> mFrameProcessor;
- static const int32_t FRAME_PROCESSOR_LISTENER_MIN_ID = 0;
- static const int32_t FRAME_PROCESSOR_LISTENER_MAX_ID = 0x7fffffffL;
std::vector<int32_t> mSupportedPhysicalRequestKeys;
@@ -245,10 +266,10 @@
/** Utility members */
binder::Status checkPidStatus(const char* checkLocation);
- binder::Status checkOperatingModeLocked(int operatingMode) const;
- binder::Status checkPhysicalCameraIdLocked(String8 physicalCameraId);
- binder::Status checkSurfaceTypeLocked(size_t numBufferProducers, bool deferredConsumer,
- int surfaceType) const;
+ static binder::Status checkOperatingMode(int operatingMode, const CameraMetadata &staticInfo,
+ const String8 &cameraId);
+ static binder::Status checkSurfaceType(size_t numBufferProducers, bool deferredConsumer,
+ int surfaceType);
static void mapStreamInfo(const OutputStreamInfo &streamInfo,
camera3_stream_rotation_t rotation, String8 physicalId,
hardware::camera::device::V3_4::Stream *stream /*out*/);
@@ -279,9 +300,9 @@
// Create a Surface from an IGraphicBufferProducer. Returns error if
// IGraphicBufferProducer's property doesn't match with streamInfo
- binder::Status createSurfaceFromGbp(OutputStreamInfo& streamInfo, bool isStreamInfoValid,
- sp<Surface>& surface, const sp<IGraphicBufferProducer>& gbp,
- const String8& physicalCameraId);
+ static binder::Status createSurfaceFromGbp(OutputStreamInfo& streamInfo, bool isStreamInfoValid,
+ sp<Surface>& surface, const sp<IGraphicBufferProducer>& gbp, const String8 &cameraId,
+ const CameraMetadata &physicalCameraMetadata);
// Utility method to insert the surface into SurfaceMap
@@ -291,7 +312,8 @@
// Check that the physicalCameraId passed in is spported by the camera
// device.
- bool checkPhysicalCameraId(const String8& physicalCameraId);
+ static binder::Status checkPhysicalCameraId(const std::vector<std::string> &physicalCameraIds,
+ const String8 &physicalCameraId, const String8 &logicalCameraId);
// IGraphicsBufferProducer binder -> Stream ID + Surface ID for output streams
KeyedVector<sp<IBinder>, StreamSurfaceId> mStreamMap;
diff --git a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp
new file mode 100644
index 0000000..1edfbf9
--- /dev/null
+++ b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "CameraOfflineClient"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include "CameraOfflineSessionClient.h"
+#include "utils/CameraThreadState.h"
+#include <utils/Trace.h>
+
+namespace android {
+
+using binder::Status;
+
+status_t CameraOfflineSessionClient::initialize(sp<CameraProviderManager>, const String8&) {
+ ATRACE_CALL();
+
+ // Verify ops permissions
+ auto res = startCameraOps();
+ if (res != OK) {
+ return res;
+ }
+
+ if (mOfflineSession.get() == nullptr) {
+ ALOGE("%s: Camera %s: No valid offline session",
+ __FUNCTION__, mCameraIdStr.string());
+ return NO_INIT;
+ }
+
+ String8 threadName;
+ mFrameProcessor = new camera2::FrameProcessorBase(mOfflineSession);
+ threadName = String8::format("Offline-%s-FrameProc", mCameraIdStr.string());
+ mFrameProcessor->run(threadName.string());
+
+ mFrameProcessor->registerListener(camera2::FrameProcessorBase::FRAME_PROCESSOR_LISTENER_MIN_ID,
+ camera2::FrameProcessorBase::FRAME_PROCESSOR_LISTENER_MAX_ID,
+ /*listener*/this,
+ /*sendPartials*/true);
+
+ wp<NotificationListener> weakThis(this);
+ res = mOfflineSession->initialize(weakThis);
+ if (res != OK) {
+ ALOGE("%s: Camera %s: unable to initialize device: %s (%d)",
+ __FUNCTION__, mCameraIdStr.string(), strerror(-res), res);
+ return res;
+ }
+
+ return OK;
+}
+
+status_t CameraOfflineSessionClient::setRotateAndCropOverride(uint8_t /*rotateAndCrop*/) {
+ // Since we're not submitting more capture requests, changes to rotateAndCrop override
+ // make no difference.
+ return OK;
+}
+
+status_t CameraOfflineSessionClient::dump(int fd, const Vector<String16>& args) {
+ return BasicClient::dump(fd, args);
+}
+
+status_t CameraOfflineSessionClient::dumpClient(int fd, const Vector<String16>& args) {
+ String8 result;
+
+ result = " Offline session dump:\n";
+ write(fd, result.string(), result.size());
+
+ if (mOfflineSession.get() == nullptr) {
+ result = " *** Offline session is detached\n";
+ write(fd, result.string(), result.size());
+ return NO_ERROR;
+ }
+
+ mFrameProcessor->dump(fd, args);
+
+ auto res = mOfflineSession->dump(fd);
+ if (res != OK) {
+ result = String8::format(" Error dumping offline session: %s (%d)",
+ strerror(-res), res);
+ write(fd, result.string(), result.size());
+ }
+
+ return OK;
+}
+
+binder::Status CameraOfflineSessionClient::disconnect() {
+ Mutex::Autolock icl(mBinderSerializationLock);
+
+ binder::Status res = Status::ok();
+ if (mDisconnected) {
+ return res;
+ }
+ // Allow both client and the media server to disconnect at all times
+ int callingPid = CameraThreadState::getCallingPid();
+ if (callingPid != mClientPid &&
+ callingPid != mServicePid) {
+ return res;
+ }
+
+ mDisconnected = true;
+
+ sCameraService->removeByClient(this);
+ sCameraService->logDisconnectedOffline(mCameraIdStr, mClientPid, String8(mClientPackageName));
+
+ sp<IBinder> remote = getRemote();
+ if (remote != nullptr) {
+ remote->unlinkToDeath(sCameraService);
+ }
+
+ mFrameProcessor->removeListener(camera2::FrameProcessorBase::FRAME_PROCESSOR_LISTENER_MIN_ID,
+ camera2::FrameProcessorBase::FRAME_PROCESSOR_LISTENER_MAX_ID,
+ /*listener*/this);
+ mFrameProcessor->requestExit();
+ mFrameProcessor->join();
+
+ finishCameraOps();
+ ALOGI("%s: Disconnected client for offline camera %s for PID %d", __FUNCTION__,
+ mCameraIdStr.string(), mClientPid);
+
+ // client shouldn't be able to call into us anymore
+ mClientPid = 0;
+
+ if (mOfflineSession.get() != nullptr) {
+ auto ret = mOfflineSession->disconnect();
+ if (ret != OK) {
+ ALOGE("%s: Failed disconnecting from offline session %s (%d)", __FUNCTION__,
+ strerror(-ret), ret);
+ }
+ mOfflineSession = nullptr;
+ }
+
+ for (size_t i = 0; i < mCompositeStreamMap.size(); i++) {
+ auto ret = mCompositeStreamMap.valueAt(i)->deleteInternalStreams();
+ if (ret != OK) {
+ ALOGE("%s: Failed removing composite stream %s (%d)", __FUNCTION__,
+ strerror(-ret), ret);
+ }
+ }
+ mCompositeStreamMap.clear();
+
+ return res;
+}
+
+void CameraOfflineSessionClient::notifyError(int32_t errorCode,
+ const CaptureResultExtras& resultExtras) {
+ // Thread safe. Don't bother locking.
+ // Composites can have multiple internal streams. Error notifications coming from such internal
+ // streams may need to remain within camera service.
+ bool skipClientNotification = false;
+ for (size_t i = 0; i < mCompositeStreamMap.size(); i++) {
+ skipClientNotification |= mCompositeStreamMap.valueAt(i)->onError(errorCode, resultExtras);
+ }
+
+ if ((mRemoteCallback.get() != nullptr) && (!skipClientNotification)) {
+ mRemoteCallback->onDeviceError(errorCode, resultExtras);
+ }
+}
+
+status_t CameraOfflineSessionClient::startCameraOps() {
+ ATRACE_CALL();
+ {
+ ALOGV("%s: Start camera ops, package name = %s, client UID = %d",
+ __FUNCTION__, String8(mClientPackageName).string(), mClientUid);
+ }
+
+ if (mAppOpsManager != nullptr) {
+ // Notify app ops that the camera is not available
+ mOpsCallback = new OpsCallback(this);
+ int32_t res;
+ // TODO : possibly change this to OP_OFFLINE_CAMERA_SESSION
+ mAppOpsManager->startWatchingMode(AppOpsManager::OP_CAMERA,
+ mClientPackageName, mOpsCallback);
+ // TODO : possibly change this to OP_OFFLINE_CAMERA_SESSION
+ res = mAppOpsManager->startOpNoThrow(AppOpsManager::OP_CAMERA,
+ mClientUid, mClientPackageName, /*startIfModeDefault*/ false);
+
+ if (res == AppOpsManager::MODE_ERRORED) {
+ ALOGI("Offline Camera %s: Access for \"%s\" has been revoked",
+ mCameraIdStr.string(), String8(mClientPackageName).string());
+ return PERMISSION_DENIED;
+ }
+
+ if (res == AppOpsManager::MODE_IGNORED) {
+ ALOGI("Offline Camera %s: Access for \"%s\" has been restricted",
+ mCameraIdStr.string(), String8(mClientPackageName).string());
+ // Return the same error as for device policy manager rejection
+ return -EACCES;
+ }
+ }
+
+ mOpsActive = true;
+
+ // Transition device state to OPEN
+ sCameraService->mUidPolicy->registerMonitorUid(mClientUid);
+
+ return OK;
+}
+
+status_t CameraOfflineSessionClient::finishCameraOps() {
+ ATRACE_CALL();
+
+ // Check if startCameraOps succeeded, and if so, finish the camera op
+ if (mOpsActive) {
+ // Notify app ops that the camera is available again
+ if (mAppOpsManager != nullptr) {
+ // TODO : possibly change this to OP_OFFLINE_CAMERA_SESSION
+ mAppOpsManager->finishOp(AppOpsManager::OP_CAMERA, mClientUid,
+ mClientPackageName);
+ mOpsActive = false;
+ }
+ }
+ // Always stop watching, even if no camera op is active
+ if (mOpsCallback != nullptr && mAppOpsManager != nullptr) {
+ mAppOpsManager->stopWatchingMode(mOpsCallback);
+ }
+ mOpsCallback.clear();
+
+ sCameraService->mUidPolicy->unregisterMonitorUid(mClientUid);
+
+ return OK;
+}
+
+void CameraOfflineSessionClient::onResultAvailable(const CaptureResult& result) {
+ ATRACE_CALL();
+ ALOGV("%s", __FUNCTION__);
+
+ if (mRemoteCallback.get() != NULL) {
+ mRemoteCallback->onResultReceived(result.mMetadata, result.mResultExtras,
+ result.mPhysicalMetadatas);
+ }
+
+ for (size_t i = 0; i < mCompositeStreamMap.size(); i++) {
+ mCompositeStreamMap.valueAt(i)->onResultAvailable(result);
+ }
+}
+
+void CameraOfflineSessionClient::notifyShutter(const CaptureResultExtras& resultExtras,
+ nsecs_t timestamp) {
+
+ if (mRemoteCallback.get() != nullptr) {
+ mRemoteCallback->onCaptureStarted(resultExtras, timestamp);
+ }
+
+ for (size_t i = 0; i < mCompositeStreamMap.size(); i++) {
+ mCompositeStreamMap.valueAt(i)->onShutter(resultExtras, timestamp);
+ }
+}
+
+void CameraOfflineSessionClient::notifyIdle() {
+ if (mRemoteCallback.get() != nullptr) {
+ mRemoteCallback->onDeviceIdle();
+ }
+}
+
+void CameraOfflineSessionClient::notifyAutoFocus(uint8_t newState, int triggerId) {
+ (void)newState;
+ (void)triggerId;
+
+ ALOGV("%s: Autofocus state now %d, last trigger %d",
+ __FUNCTION__, newState, triggerId);
+}
+
+void CameraOfflineSessionClient::notifyAutoExposure(uint8_t newState, int triggerId) {
+ (void)newState;
+ (void)triggerId;
+
+ ALOGV("%s: Autoexposure state now %d, last trigger %d",
+ __FUNCTION__, newState, triggerId);
+}
+
+void CameraOfflineSessionClient::notifyAutoWhitebalance(uint8_t newState, int triggerId) {
+ (void)newState;
+ (void)triggerId;
+
+ ALOGV("%s: Auto-whitebalance state now %d, last trigger %d", __FUNCTION__, newState,
+ triggerId);
+}
+
+void CameraOfflineSessionClient::notifyPrepared(int /*streamId*/) {
+ ALOGE("%s: Unexpected stream prepare notification in offline mode!", __FUNCTION__);
+ notifyError(hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DEVICE,
+ CaptureResultExtras());
+}
+
+void CameraOfflineSessionClient::notifyRequestQueueEmpty() {
+ if (mRemoteCallback.get() != nullptr) {
+ mRemoteCallback->onRequestQueueEmpty();
+ }
+}
+
+void CameraOfflineSessionClient::notifyRepeatingRequestError(long /*lastFrameNumber*/) {
+ ALOGE("%s: Unexpected repeating request error in offline mode!", __FUNCTION__);
+ notifyError(hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DEVICE,
+ CaptureResultExtras());
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h
new file mode 100644
index 0000000..b67fcb3
--- /dev/null
+++ b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SERVERS_CAMERA_PHOTOGRAPHY_CAMERAOFFLINESESSIONCLIENT_H
+#define ANDROID_SERVERS_CAMERA_PHOTOGRAPHY_CAMERAOFFLINESESSIONCLIENT_H
+
+#include <android/hardware/camera2/BnCameraOfflineSession.h>
+#include <android/hardware/camera2/ICameraDeviceCallbacks.h>
+#include "common/FrameProcessorBase.h"
+#include "common/CameraDeviceBase.h"
+#include "CameraService.h"
+#include "CompositeStream.h"
+
+namespace android {
+
+using android::hardware::camera2::ICameraDeviceCallbacks;
+using camera3::CompositeStream;
+
+// Client for offline session. Note that offline session client does not affect camera service's
+// client arbitration logic. It is camera HAL's decision to decide whether a normal camera
+// client is conflicting with existing offline client(s).
+// The other distinctive difference between offline clients and normal clients is that normal
+// clients are created through ICameraService binder calls, while the offline session client
+// is created through ICameraDeviceUser::switchToOffline call.
+class CameraOfflineSessionClient :
+ public CameraService::BasicClient,
+ public hardware::camera2::BnCameraOfflineSession,
+ public camera2::FrameProcessorBase::FilteredListener,
+ public NotificationListener
+{
+public:
+ CameraOfflineSessionClient(
+ const sp<CameraService>& cameraService,
+ sp<CameraOfflineSessionBase> session,
+ const KeyedVector<sp<IBinder>, sp<CompositeStream>>& offlineCompositeStreamMap,
+ const sp<ICameraDeviceCallbacks>& remoteCallback,
+ const String16& clientPackageName,
+ const std::unique_ptr<String16>& clientFeatureId,
+ const String8& cameraIdStr, int cameraFacing,
+ int clientPid, uid_t clientUid, int servicePid) :
+ CameraService::BasicClient(
+ cameraService,
+ IInterface::asBinder(remoteCallback),
+ clientPackageName, clientFeatureId,
+ cameraIdStr, cameraFacing, clientPid, clientUid, servicePid),
+ mRemoteCallback(remoteCallback), mOfflineSession(session),
+ mCompositeStreamMap(offlineCompositeStreamMap) {}
+
+ virtual ~CameraOfflineSessionClient() {}
+
+ sp<IBinder> asBinderWrapper() override {
+ return IInterface::asBinder(this);
+ }
+
+ binder::Status disconnect() override;
+
+ status_t dump(int /*fd*/, const Vector<String16>& /*args*/) override;
+
+ status_t dumpClient(int /*fd*/, const Vector<String16>& /*args*/) override;
+
+ status_t initialize(sp<CameraProviderManager> /*manager*/,
+ const String8& /*monitorTags*/) override;
+
+ status_t setRotateAndCropOverride(uint8_t rotateAndCrop) override;
+
+ // permissions management
+ status_t startCameraOps() override;
+ status_t finishCameraOps() override;
+
+ // FilteredResultListener API
+ void onResultAvailable(const CaptureResult& result) override;
+
+ // NotificationListener API
+ void notifyError(int32_t errorCode, const CaptureResultExtras& resultExtras) override;
+ void notifyShutter(const CaptureResultExtras& resultExtras, nsecs_t timestamp) override;
+ void notifyIdle() override;
+ void notifyAutoFocus(uint8_t newState, int triggerId) override;
+ void notifyAutoExposure(uint8_t newState, int triggerId) override;
+ void notifyAutoWhitebalance(uint8_t newState, int triggerId) override;
+ void notifyPrepared(int streamId) override;
+ void notifyRequestQueueEmpty() override;
+ void notifyRepeatingRequestError(long lastFrameNumber) override;
+
+private:
+ mutable Mutex mBinderSerializationLock;
+
+ sp<hardware::camera2::ICameraDeviceCallbacks> mRemoteCallback;
+
+ sp<CameraOfflineSessionBase> mOfflineSession;
+
+ sp<camera2::FrameProcessorBase> mFrameProcessor;
+
+ // Offline composite stream map, output surface -> composite stream
+ KeyedVector<sp<IBinder>, sp<CompositeStream>> mCompositeStreamMap;
+};
+
+} // namespace android
+
+#endif // ANDROID_SERVERS_CAMERA_PHOTOGRAPHY_CAMERAOFFLINESESSIONCLIENT_H
diff --git a/services/camera/libcameraservice/api2/CompositeStream.h b/services/camera/libcameraservice/api2/CompositeStream.h
index a401a82..de894f3 100644
--- a/services/camera/libcameraservice/api2/CompositeStream.h
+++ b/services/camera/libcameraservice/api2/CompositeStream.h
@@ -64,6 +64,10 @@
virtual status_t insertGbp(SurfaceMap* /*out*/outSurfaceMap,
Vector<int32_t>* /*out*/outputStreamIds, int32_t* /*out*/currentStreamId) = 0;
+ // Attach the internal composite stream ids.
+ virtual status_t insertCompositeStreamIds(
+ std::vector<int32_t>* compositeStreamIds /*out*/) = 0;
+
// Return composite stream id.
virtual int getStreamId() = 0;
diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
index 0b91016..acad8c6 100644
--- a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
@@ -20,7 +20,6 @@
#include "api1/client2/JpegProcessor.h"
#include "common/CameraProviderManager.h"
-#include "dlfcn.h"
#include <gui/Surface.h>
#include <utils/Log.h>
#include <utils/Trace.h>
@@ -43,9 +42,7 @@
mBlobBufferAcquired(false),
mProducerListener(new ProducerListener()),
mMaxJpegSize(-1),
- mIsLogicalCamera(false),
- mDepthPhotoLibHandle(nullptr),
- mDepthPhotoProcess(nullptr) {
+ mIsLogicalCamera(false) {
sp<CameraDeviceBase> cameraDevice = device.promote();
if (cameraDevice.get() != nullptr) {
CameraMetadata staticInfo = cameraDevice->info();
@@ -83,19 +80,6 @@
}
getSupportedDepthSizes(staticInfo, &mSupportedDepthSizes);
-
- mDepthPhotoLibHandle = dlopen(camera3::kDepthPhotoLibrary, RTLD_NOW | RTLD_LOCAL);
- if (mDepthPhotoLibHandle != nullptr) {
- mDepthPhotoProcess = reinterpret_cast<camera3::process_depth_photo_frame> (
- dlsym(mDepthPhotoLibHandle, camera3::kDepthPhotoProcessFunction));
- if (mDepthPhotoProcess == nullptr) {
- ALOGE("%s: Failed to link to depth photo process function: %s", __FUNCTION__,
- dlerror());
- }
- } else {
- ALOGE("%s: Failed to link to depth photo library: %s", __FUNCTION__, dlerror());
- }
-
}
}
@@ -108,11 +92,6 @@
mDepthSurface.clear();
mDepthConsumer = nullptr;
mDepthSurface = nullptr;
- if (mDepthPhotoLibHandle != nullptr) {
- dlclose(mDepthPhotoLibHandle);
- mDepthPhotoLibHandle = nullptr;
- }
- mDepthPhotoProcess = nullptr;
}
void DepthCompositeStream::compilePendingInputLocked() {
@@ -356,7 +335,7 @@
}
size_t actualJpegSize = 0;
- res = mDepthPhotoProcess(depthPhoto, finalJpegBufferSize, dstBuffer, &actualJpegSize);
+ res = processDepthPhotoFrame(depthPhoto, finalJpegBufferSize, dstBuffer, &actualJpegSize);
if (res != 0) {
ALOGE("%s: Depth photo processing failed: %s (%d)", __FUNCTION__, strerror(-res), res);
outputANW->cancelBuffer(mOutputSurface.get(), anb, /*fence*/ -1);
@@ -583,11 +562,6 @@
return NO_ERROR;
}
- if ((mDepthPhotoLibHandle == nullptr) || (mDepthPhotoProcess == nullptr)) {
- ALOGE("%s: Depth photo library is not present!", __FUNCTION__);
- return NO_INIT;
- }
-
if (mOutputSurface.get() == nullptr) {
ALOGE("%s: No valid output surface set!", __FUNCTION__);
return NO_INIT;
@@ -709,6 +683,18 @@
return NO_ERROR;
}
+status_t DepthCompositeStream::insertCompositeStreamIds(
+ std::vector<int32_t>* compositeStreamIds /*out*/) {
+ if (compositeStreamIds == nullptr) {
+ return BAD_VALUE;
+ }
+
+ compositeStreamIds->push_back(mDepthStreamId);
+ compositeStreamIds->push_back(mBlobStreamId);
+
+ return OK;
+}
+
void DepthCompositeStream::onResultError(const CaptureResultExtras& resultExtras) {
// Processing can continue even in case of result errors.
// At the moment depth composite stream processing relies mainly on static camera
diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.h b/services/camera/libcameraservice/api2/DepthCompositeStream.h
index 28a7826..1bf714d 100644
--- a/services/camera/libcameraservice/api2/DepthCompositeStream.h
+++ b/services/camera/libcameraservice/api2/DepthCompositeStream.h
@@ -56,6 +56,7 @@
status_t configureStream() override;
status_t insertGbp(SurfaceMap* /*out*/outSurfaceMap, Vector<int32_t>* /*out*/outputStreamIds,
int32_t* /*out*/currentStreamId) override;
+ status_t insertCompositeStreamIds(std::vector<int32_t>* compositeStreamIds /*out*/) override;
int getStreamId() override { return mBlobStreamId; }
// CpuConsumer listener implementation
@@ -126,8 +127,6 @@
std::vector<std::tuple<size_t, size_t>> mSupportedDepthSizes;
std::vector<float> mIntrinsicCalibration, mLensDistortion;
bool mIsLogicalCamera;
- void* mDepthPhotoLibHandle;
- process_depth_photo_frame mDepthPhotoProcess;
// Keep all incoming Depth buffer timestamps pending further processing.
std::vector<int64_t> mInputDepthBuffers;
diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
index bf4d1c8..d25e467 100644
--- a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
@@ -67,6 +67,7 @@
mDequeuedOutputBufferCnt(0),
mLockedAppSegmentBufferCnt(0),
mCodecOutputCounter(0),
+ mQuality(-1),
mGridTimestampUs(0) {
}
@@ -503,6 +504,18 @@
return NO_ERROR;
}
+status_t HeicCompositeStream::insertCompositeStreamIds(
+ std::vector<int32_t>* compositeStreamIds /*out*/) {
+ if (compositeStreamIds == nullptr) {
+ return BAD_VALUE;
+ }
+
+ compositeStreamIds->push_back(mAppSegmentStreamId);
+ compositeStreamIds->push_back(mMainImageStreamId);
+
+ return OK;
+}
+
void HeicCompositeStream::onShutter(const CaptureResultExtras& resultExtras, nsecs_t timestamp) {
Mutex::Autolock l(mMutex);
if (mErrorState) {
@@ -525,6 +538,12 @@
mPendingInputFrames[it->first].orientation = it->second.first;
mPendingInputFrames[it->first].quality = it->second.second;
mSettingsByTimestamp.erase(it);
+
+ // Set encoder quality if no inflight encoding
+ if (mPendingInputFrames.size() == 1) {
+ int32_t newQuality = mPendingInputFrames.begin()->second.quality;
+ updateCodecQualityLocked(newQuality);
+ }
}
while (!mInputAppSegmentBuffers.empty()) {
@@ -851,17 +870,6 @@
strerror(-res), res);
return res;
}
- // Set encoder quality
- {
- sp<AMessage> qualityParams = new AMessage;
- qualityParams->setInt32(PARAMETER_KEY_VIDEO_BITRATE, inputFrame.quality);
- res = mCodec->setParameters(qualityParams);
- if (res != OK) {
- ALOGE("%s: Failed to set codec quality: %s (%d)",
- __FUNCTION__, strerror(-res), res);
- return res;
- }
- }
ssize_t trackId = inputFrame.muxer->addTrack(inputFrame.format);
if (trackId < 0) {
@@ -1148,16 +1156,30 @@
void HeicCompositeStream::releaseInputFramesLocked() {
auto it = mPendingInputFrames.begin();
+ bool inputFrameDone = false;
while (it != mPendingInputFrames.end()) {
auto& inputFrame = it->second;
if (inputFrame.error ||
(inputFrame.appSegmentWritten && inputFrame.pendingOutputTiles == 0)) {
releaseInputFrameLocked(&inputFrame);
it = mPendingInputFrames.erase(it);
+ inputFrameDone = true;
} else {
it++;
}
}
+
+ // Update codec quality based on first upcoming input frame.
+ // Note that when encoding is in surface mode, currently there is no
+ // way for camera service to synchronize quality setting on a per-frame
+ // basis: we don't get notification when codec is ready to consume a new
+ // input frame. So we update codec quality on a best-effort basis.
+ if (inputFrameDone) {
+ auto firstPendingFrame = mPendingInputFrames.begin();
+ if (firstPendingFrame != mPendingInputFrames.end()) {
+ updateCodecQualityLocked(firstPendingFrame->second.quality);
+ }
+ }
}
status_t HeicCompositeStream::initializeCodec(uint32_t width, uint32_t height,
@@ -1546,6 +1568,20 @@
return maxAppsSegment * (2 + 0xFFFF) + sizeof(struct CameraBlob);
}
+void HeicCompositeStream::updateCodecQualityLocked(int32_t quality) {
+ if (quality != mQuality) {
+ sp<AMessage> qualityParams = new AMessage;
+ qualityParams->setInt32(PARAMETER_KEY_VIDEO_BITRATE, quality);
+ status_t res = mCodec->setParameters(qualityParams);
+ if (res != OK) {
+ ALOGE("%s: Failed to set codec quality: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ } else {
+ mQuality = quality;
+ }
+ }
+}
+
bool HeicCompositeStream::threadLoop() {
int64_t currentTs = INT64_MAX;
bool newInputAvailable = false;
diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.h b/services/camera/libcameraservice/api2/HeicCompositeStream.h
index 04e7b83..8fc521e 100644
--- a/services/camera/libcameraservice/api2/HeicCompositeStream.h
+++ b/services/camera/libcameraservice/api2/HeicCompositeStream.h
@@ -55,6 +55,8 @@
status_t insertGbp(SurfaceMap* /*out*/outSurfaceMap, Vector<int32_t>* /*out*/outputStreamIds,
int32_t* /*out*/currentStreamId) override;
+ status_t insertCompositeStreamIds(std::vector<int32_t>* compositeStreamIds /*out*/) override;
+
void onShutter(const CaptureResultExtras& resultExtras, nsecs_t timestamp) override;
int getStreamId() override { return mMainImageStreamId; }
@@ -199,6 +201,7 @@
size_t top, size_t left, size_t width, size_t height);
void initCopyRowFunction(int32_t width);
static size_t calcAppSegmentMaxSize(const CameraMetadata& info);
+ void updateCodecQualityLocked(int32_t quality);
static const nsecs_t kWaitDuration = 10000000; // 10 ms
static const int32_t kDefaultJpegQuality = 99;
@@ -240,6 +243,7 @@
std::vector<CodecOutputBufferInfo> mCodecOutputBuffers;
std::queue<int64_t> mCodecOutputBufferTimestamps;
size_t mCodecOutputCounter;
+ int32_t mQuality;
// Keep all incoming Yuv buffer pending tiling and encoding (for HEVC YUV tiling only)
std::vector<int64_t> mInputYuvBuffers;
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
index 8792f9a..0a41776 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -111,7 +111,7 @@
return res;
}
- wp<CameraDeviceBase::NotificationListener> weakThis(this);
+ wp<NotificationListener> weakThis(this);
res = mDevice->setNotifyCallback(weakThis);
return OK;
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h
index 12cba0b..042f5aa 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.h
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.h
@@ -29,7 +29,7 @@
template <typename TClientBase>
class Camera2ClientBase :
public TClientBase,
- public CameraDeviceBase::NotificationListener
+ public NotificationListener
{
public:
typedef typename TClientBase::TCamCallbacks TCamCallbacks;
@@ -61,7 +61,7 @@
virtual status_t dumpClient(int fd, const Vector<String16>& args);
/**
- * CameraDeviceBase::NotificationListener implementation
+ * NotificationListener implementation
*/
virtual void notifyError(int32_t errorCode,
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.cpp b/services/camera/libcameraservice/common/CameraDeviceBase.cpp
index 6c4e87f..0efe4bc 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.cpp
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.cpp
@@ -24,7 +24,4 @@
CameraDeviceBase::~CameraDeviceBase() {
}
-CameraDeviceBase::NotificationListener::~NotificationListener() {
-}
-
} // namespace android
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 935bc37..3662a65 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -34,6 +34,9 @@
#include "gui/IGraphicBufferProducer.h"
#include "device3/Camera3StreamInterface.h"
#include "binder/Status.h"
+#include "FrameProducer.h"
+
+#include "CameraOfflineSessionBase.h"
namespace android {
@@ -46,16 +49,11 @@
* Base interface for version >= 2 camera device classes, which interface to
* camera HAL device versions >= 2.
*/
-class CameraDeviceBase : public virtual RefBase {
+class CameraDeviceBase : public virtual FrameProducer {
public:
virtual ~CameraDeviceBase();
/**
- * The device's camera ID
- */
- virtual const String8& getId() const = 0;
-
- /**
* The device vendor tag ID
*/
virtual metadata_vendor_id_t getVendorTagId() const = 0;
@@ -66,13 +64,9 @@
virtual status_t dump(int fd, const Vector<String16> &args) = 0;
/**
- * The device's static characteristics metadata buffer
- */
- virtual const CameraMetadata& info() const = 0;
- /**
* The physical camera device's static characteristics metadata buffer
*/
- virtual const CameraMetadata& info(const String8& physicalId) const = 0;
+ virtual const CameraMetadata& infoPhysical(const String8& physicalId) const = 0;
struct PhysicalCameraSettings {
std::string cameraId;
@@ -232,6 +226,12 @@
virtual status_t configureStreams(const CameraMetadata& sessionParams,
int operatingMode = 0) = 0;
+ /**
+ * Retrieve a list of all stream ids that were advertised as capable of
+ * supporting offline processing mode by Hal after the last stream configuration.
+ */
+ virtual void getOfflineStreamIds(std::vector<int> *offlineStreamIds) = 0;
+
// get the buffer producer of the input stream
virtual status_t getInputBufferProducer(
sp<IGraphicBufferProducer> *producer) = 0;
@@ -257,35 +257,6 @@
virtual ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const = 0;
/**
- * Abstract class for HAL notification listeners
- */
- class NotificationListener : public virtual RefBase {
- public:
- // The set of notifications is a merge of the notifications required for
- // API1 and API2.
-
- // Required for API 1 and 2
- virtual void notifyError(int32_t errorCode,
- const CaptureResultExtras &resultExtras) = 0;
-
- // Required only for API2
- virtual void notifyIdle() = 0;
- virtual void notifyShutter(const CaptureResultExtras &resultExtras,
- nsecs_t timestamp) = 0;
- virtual void notifyPrepared(int streamId) = 0;
- virtual void notifyRequestQueueEmpty() = 0;
-
- // Required only for API1
- virtual void notifyAutoFocus(uint8_t newState, int triggerId) = 0;
- virtual void notifyAutoExposure(uint8_t newState, int triggerId) = 0;
- virtual void notifyAutoWhitebalance(uint8_t newState,
- int triggerId) = 0;
- virtual void notifyRepeatingRequestError(long lastFrameNumber) = 0;
- protected:
- virtual ~NotificationListener();
- };
-
- /**
* Connect HAL notifications to a listener. Overwrites previous
* listener. Set to NULL to stop receiving notifications.
*/
@@ -299,21 +270,6 @@
virtual bool willNotify3A() = 0;
/**
- * Wait for a new frame to be produced, with timeout in nanoseconds.
- * Returns TIMED_OUT when no frame produced within the specified duration
- * May be called concurrently to most methods, except for getNextFrame
- */
- virtual status_t waitForNextFrame(nsecs_t timeout) = 0;
-
- /**
- * Get next capture result frame from the result queue. Returns NOT_ENOUGH_DATA
- * if the queue is empty; caller takes ownership of the metadata buffer inside
- * the capture result object's metadata field.
- * May be called concurrently to most methods, except for waitForNextFrame.
- */
- virtual status_t getNextResult(CaptureResult *frame) = 0;
-
- /**
* Trigger auto-focus. The latest ID used in a trigger autofocus or cancel
* autofocus call will be returned by the HAL in all subsequent AF
* notifications.
@@ -389,6 +345,23 @@
* requests to complete, based on their settings
*/
virtual nsecs_t getExpectedInFlightDuration() = 0;
+
+ /**
+ * switch to offline session
+ */
+ virtual status_t switchToOffline(
+ const std::vector<int32_t>& streamsToKeep,
+ /*out*/ sp<CameraOfflineSessionBase>* session) = 0;
+
+ /**
+ * Set the current behavior for the ROTATE_AND_CROP control when in AUTO.
+ *
+ * The value must be one of the ROTATE_AND_CROP_* values besides AUTO,
+ * and defaults to NONE.
+ */
+ virtual status_t setRotateAndCropAutoBehavior(
+ camera_metadata_enum_android_scaler_rotate_and_crop_t rotateAndCropValue) = 0;
+
};
}; // namespace android
diff --git a/services/camera/libcameraservice/common/CameraOfflineSessionBase.cpp b/services/camera/libcameraservice/common/CameraOfflineSessionBase.cpp
new file mode 100644
index 0000000..ff673a9
--- /dev/null
+++ b/services/camera/libcameraservice/common/CameraOfflineSessionBase.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CameraOfflineSessionBase.h"
+
+namespace android {
+
+/**
+ * Base class destructors
+ */
+CameraOfflineSessionBase::~CameraOfflineSessionBase() {
+}
+
+} // namespace android
diff --git a/services/camera/libcameraservice/common/CameraOfflineSessionBase.h b/services/camera/libcameraservice/common/CameraOfflineSessionBase.h
new file mode 100644
index 0000000..1f835a9
--- /dev/null
+++ b/services/camera/libcameraservice/common/CameraOfflineSessionBase.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SERVERS_CAMERA_CAMERAOFFLINESESSIONBASE_H
+#define ANDROID_SERVERS_CAMERA_CAMERAOFFLINESESSIONBASE_H
+
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <utils/Timers.h>
+
+#include "camera/CaptureResult.h"
+#include "FrameProducer.h"
+
+namespace android {
+
+/**
+ * Abstract class for HAL notification listeners
+ */
+class NotificationListener : public virtual RefBase {
+ public:
+ // The set of notifications is a merge of the notifications required for
+ // API1 and API2.
+
+ // Required for API 1 and 2
+ virtual void notifyError(int32_t errorCode,
+ const CaptureResultExtras &resultExtras) = 0;
+
+ // Required only for API2
+ virtual void notifyIdle() = 0;
+ virtual void notifyShutter(const CaptureResultExtras &resultExtras,
+ nsecs_t timestamp) = 0;
+ virtual void notifyPrepared(int streamId) = 0;
+ virtual void notifyRequestQueueEmpty() = 0;
+
+ // Required only for API1
+ virtual void notifyAutoFocus(uint8_t newState, int triggerId) = 0;
+ virtual void notifyAutoExposure(uint8_t newState, int triggerId) = 0;
+ virtual void notifyAutoWhitebalance(uint8_t newState,
+ int triggerId) = 0;
+ virtual void notifyRepeatingRequestError(long lastFrameNumber) = 0;
+ protected:
+ virtual ~NotificationListener() {}
+};
+
+class CameraOfflineSessionBase : public virtual FrameProducer {
+ public:
+ virtual ~CameraOfflineSessionBase();
+
+ virtual status_t initialize(
+ wp<NotificationListener> listener) = 0;
+
+ virtual status_t disconnect() = 0;
+
+ virtual status_t dump(int fd) = 0;
+
+ // TODO: notification passing path
+}; // class CameraOfflineSessionBase
+
+} // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index 7ed4c3d..b20774a 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -37,15 +37,19 @@
#include <android-base/logging.h>
#include <cutils/properties.h>
#include <hwbinder/IPCThreadState.h>
+#include <utils/SessionConfigurationUtils.h>
#include <utils/Trace.h>
#include "api2/HeicCompositeStream.h"
+#include "device3/ZoomRatioMapper.h"
namespace android {
using namespace ::android::hardware::camera;
using namespace ::android::hardware::camera::common::V1_0;
using std::literals::chrono_literals::operator""s;
+using hardware::camera2::utils::CameraIdAndSessionConfiguration;
+using hardware::camera::provider::V2_6::CameraIdAndStreamCombination;
namespace {
const bool kEnableLazyHal(property_get_bool("ro.camera.enableLazyHal", false));
@@ -232,6 +236,15 @@
return deviceInfo->hasFlashUnit();
}
+bool CameraProviderManager::supportNativeZoomRatio(const std::string &id) const {
+ std::lock_guard<std::mutex> lock(mInterfaceMutex);
+
+ auto deviceInfo = findDeviceInfoLocked(id);
+ if (deviceInfo == nullptr) return false;
+
+ return deviceInfo->supportNativeZoomRatio();
+}
+
status_t CameraProviderManager::getResourceCost(const std::string &id,
CameraResourceCost* cost) const {
std::lock_guard<std::mutex> lock(mInterfaceMutex);
@@ -257,7 +270,6 @@
const hardware::camera::device::V3_4::StreamConfiguration &configuration,
bool *status /*out*/) const {
std::lock_guard<std::mutex> lock(mInterfaceMutex);
-
auto deviceInfo = findDeviceInfoLocked(id);
if (deviceInfo == nullptr) {
return NAME_NOT_FOUND;
@@ -706,31 +718,6 @@
}
}
-bool CameraProviderManager::ProviderInfo::DeviceInfo3::isDepthPhotoLibraryPresent() {
- static bool libraryPresent = false;
- static bool initialized = false;
- if (initialized) {
- return libraryPresent;
- } else {
- initialized = true;
- }
-
- void* depthLibHandle = dlopen(camera3::kDepthPhotoLibrary, RTLD_NOW | RTLD_LOCAL);
- if (depthLibHandle == nullptr) {
- return false;
- }
-
- auto processFunc = dlsym(depthLibHandle, camera3::kDepthPhotoProcessFunction);
- if (processFunc != nullptr) {
- libraryPresent = true;
- } else {
- libraryPresent = false;
- }
- dlclose(depthLibHandle);
-
- return libraryPresent;
-}
-
status_t CameraProviderManager::ProviderInfo::DeviceInfo3::addDynamicDepthTags() {
uint32_t depthExclTag = ANDROID_DEPTH_DEPTH_IS_EXCLUSIVE;
uint32_t depthSizesTag = ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS;
@@ -778,11 +765,6 @@
return OK;
}
- if(!isDepthPhotoLibraryPresent()) {
- // Depth photo processing library is not present, nothing more to do.
- return OK;
- }
-
std::vector<int32_t> dynamicDepthEntries;
for (const auto& it : supportedDynamicDepthSizes) {
int32_t entry[4] = {HAL_PIXEL_FORMAT_BLOB, static_cast<int32_t> (std::get<0>(it)),
@@ -946,6 +928,19 @@
return res;
}
+status_t CameraProviderManager::ProviderInfo::DeviceInfo3::addRotateCropTags() {
+ status_t res = OK;
+ auto& c = mCameraCharacteristics;
+
+ auto availableRotateCropEntry = c.find(ANDROID_SCALER_AVAILABLE_ROTATE_AND_CROP_MODES);
+ if (availableRotateCropEntry.count == 0) {
+ uint8_t defaultAvailableRotateCropEntry = ANDROID_SCALER_ROTATE_AND_CROP_NONE;
+ res = c.update(ANDROID_SCALER_AVAILABLE_ROTATE_AND_CROP_MODES,
+ &defaultAvailableRotateCropEntry, 1);
+ }
+ return res;
+}
+
status_t CameraProviderManager::ProviderInfo::DeviceInfo3::removeAvailableKeys(
CameraMetadata& c, const std::vector<uint32_t>& keys, uint32_t keyTag) {
status_t res = OK;
@@ -1090,10 +1085,8 @@
return OK;
}
-bool CameraProviderManager::isLogicalCamera(const std::string& id,
+bool CameraProviderManager::isLogicalCameraLocked(const std::string& id,
std::vector<std::string>* physicalCameraIds) {
- std::lock_guard<std::mutex> lock(mInterfaceMutex);
-
auto deviceInfo = findDeviceInfoLocked(id);
if (deviceInfo == nullptr) return false;
@@ -1103,6 +1096,12 @@
return deviceInfo->mIsLogicalCamera;
}
+bool CameraProviderManager::isLogicalCamera(const std::string& id,
+ std::vector<std::string>* physicalCameraIds) {
+ std::lock_guard<std::mutex> lock(mInterfaceMutex);
+ return isLogicalCameraLocked(id, physicalCameraIds);
+}
+
status_t CameraProviderManager::getSystemCameraKind(const std::string& id,
SystemCameraKind *kind) const {
std::lock_guard<std::mutex> lock(mInterfaceMutex);
@@ -1268,25 +1267,27 @@
mProviderName.c_str(), interface->isRemote());
// Determine minor version
- auto castResult = provider::V2_5::ICameraProvider::castFrom(interface);
- if (castResult.isOk()) {
- mMinorVersion = 5;
- } else {
- mMinorVersion = 4;
+ mMinorVersion = 4;
+ auto cast2_6 = provider::V2_6::ICameraProvider::castFrom(interface);
+ sp<provider::V2_6::ICameraProvider> interface2_6 = nullptr;
+ if (cast2_6.isOk()) {
+ interface2_6 = cast2_6;
+ if (interface2_6 != nullptr) {
+ mMinorVersion = 6;
+ }
}
-
- // cameraDeviceStatusChange callbacks may be called (and causing new devices added)
- // before setCallback returns
- hardware::Return<Status> status = interface->setCallback(this);
- if (!status.isOk()) {
- ALOGE("%s: Transaction error setting up callbacks with camera provider '%s': %s",
- __FUNCTION__, mProviderName.c_str(), status.description().c_str());
- return DEAD_OBJECT;
- }
- if (status != Status::OK) {
- ALOGE("%s: Unable to register callbacks with camera provider '%s'",
- __FUNCTION__, mProviderName.c_str());
- return mapToStatusT(status);
+ // We need to check again since cast2_6.isOk() succeeds even if the provider
+ // version isn't actually 2.6.
+ if (interface2_6 == nullptr){
+ auto cast2_5 =
+ provider::V2_5::ICameraProvider::castFrom(interface);
+ sp<provider::V2_5::ICameraProvider> interface2_5 = nullptr;
+ if (cast2_5.isOk()) {
+ interface2_5 = cast2_5;
+ if (interface != nullptr) {
+ mMinorVersion = 5;
+ }
+ }
}
hardware::Return<bool> linked = interface->linkToDeath(this, /*cookie*/ mId);
@@ -1317,6 +1318,7 @@
return res;
}
+ Status status;
// Get initial list of camera devices, if any
std::vector<std::string> devices;
hardware::Return<void> ret = interface->getCameraIdList([&status, this, &devices](
@@ -1348,6 +1350,34 @@
return mapToStatusT(status);
}
+ // Get list of concurrent streaming camera device combinations
+ if (mMinorVersion >= 6) {
+ hardware::Return<void> ret = interface2_6->getConcurrentStreamingCameraIds([&status, this](
+ Status concurrentIdStatus, // TODO: Move all instances of hidl_string to 'using'
+ const hardware::hidl_vec<hardware::hidl_vec<hardware::hidl_string>>&
+ cameraDeviceIdCombinations) {
+ status = concurrentIdStatus;
+ if (status == Status::OK) {
+ for (auto& combination : cameraDeviceIdCombinations) {
+ std::unordered_set<std::string> deviceIds;
+ for (auto &cameraDeviceId : combination) {
+ deviceIds.insert(cameraDeviceId.c_str());
+ }
+ mConcurrentCameraIdCombinations.push_back(std::move(deviceIds));
+ }
+ } });
+ if (!ret.isOk()) {
+ ALOGE("%s: Transaction error in getting camera ID list from provider '%s': %s",
+ __FUNCTION__, mProviderName.c_str(), linked.description().c_str());
+ return DEAD_OBJECT;
+ }
+ if (status != Status::OK) {
+ ALOGE("%s: Unable to query for camera devices from provider '%s'",
+ __FUNCTION__, mProviderName.c_str());
+ return mapToStatusT(status);
+ }
+ }
+
ret = interface->isSetTorchModeSupported(
[this](auto status, bool supported) {
if (status == Status::OK) {
@@ -1373,6 +1403,22 @@
}
}
+ // cameraDeviceStatusChange callbacks may be called (and causing new devices added)
+ // before setCallback returns. setCallback must be called after addDevice so that
+ // the physical camera status callback can look up available regular
+ // cameras.
+ hardware::Return<Status> st = interface->setCallback(this);
+ if (!st.isOk()) {
+ ALOGE("%s: Transaction error setting up callbacks with camera provider '%s': %s",
+ __FUNCTION__, mProviderName.c_str(), st.description().c_str());
+ return DEAD_OBJECT;
+ }
+ if (st != Status::OK) {
+ ALOGE("%s: Unable to register callbacks with camera provider '%s'",
+ __FUNCTION__, mProviderName.c_str());
+ return mapToStatusT(st);
+ }
+
ALOGI("Camera provider %s ready with %zu camera devices",
mProviderName.c_str(), mDevices.size());
@@ -1624,6 +1670,61 @@
return hardware::Void();
}
+hardware::Return<void> CameraProviderManager::ProviderInfo::physicalCameraDeviceStatusChange(
+ const hardware::hidl_string& cameraDeviceName,
+ const hardware::hidl_string& physicalCameraDeviceName,
+ CameraDeviceStatus newStatus) {
+ sp<StatusListener> listener;
+ std::string id;
+ bool initialized = false;
+ {
+ std::lock_guard<std::mutex> lock(mLock);
+ bool known = false;
+ for (auto& deviceInfo : mDevices) {
+ if (deviceInfo->mName == cameraDeviceName) {
+ id = deviceInfo->mId;
+
+ if (!deviceInfo->mIsLogicalCamera) {
+ ALOGE("%s: Invalid combination of camera id %s, physical id %s",
+ __FUNCTION__, id.c_str(), physicalCameraDeviceName.c_str());
+ return hardware::Void();
+ }
+ if (std::find(deviceInfo->mPhysicalIds.begin(), deviceInfo->mPhysicalIds.end(),
+ physicalCameraDeviceName) == deviceInfo->mPhysicalIds.end()) {
+ ALOGE("%s: Invalid combination of camera id %s, physical id %s",
+ __FUNCTION__, id.c_str(), physicalCameraDeviceName.c_str());
+ return hardware::Void();
+ }
+ ALOGI("Camera device %s physical device %s status is now %s, was %s",
+ cameraDeviceName.c_str(), physicalCameraDeviceName.c_str(),
+ deviceStatusToString(newStatus), deviceStatusToString(
+ deviceInfo->mPhysicalStatus[physicalCameraDeviceName]));
+ known = true;
+ break;
+ }
+ }
+ // Previously unseen device; status must not be NOT_PRESENT
+ if (!known) {
+ ALOGW("Camera provider %s says an unknown camera device %s-%s is not present. Curious.",
+ mProviderName.c_str(), cameraDeviceName.c_str(),
+ physicalCameraDeviceName.c_str());
+ return hardware::Void();
+ }
+ listener = mManager->getStatusListener();
+ initialized = mInitialized;
+ }
+ // Call without lock held to allow reentrancy into provider manager
+ // Don't send the callback if providerInfo hasn't been initialized.
+ // CameraService will initialize device status after provider is
+ // initialized
+ if (listener != nullptr && initialized) {
+ String8 physicalId(physicalCameraDeviceName.c_str());
+ listener->onDeviceStatusChanged(String8(id.c_str()),
+ physicalId, newStatus);
+ }
+ return hardware::Void();
+}
+
hardware::Return<void> CameraProviderManager::ProviderInfo::torchModeStatusChange(
const hardware::hidl_string& cameraDeviceName,
TorchModeStatus newStatus) {
@@ -1729,6 +1830,55 @@
return OK;
}
+status_t CameraProviderManager::ProviderInfo::isConcurrentSessionConfigurationSupported(
+ const hardware::hidl_vec<CameraIdAndStreamCombination> &halCameraIdsAndStreamCombinations,
+ bool *isSupported) {
+ status_t res = OK;
+ if (mMinorVersion >= 6) {
+ // Check if the provider is currently active - not going to start it up for this notification
+ auto interface = mSavedInterface != nullptr ? mSavedInterface : mActiveInterface.promote();
+ if (interface == nullptr) {
+ // TODO: This might be some other problem
+ return INVALID_OPERATION;
+ }
+ auto castResult = provider::V2_6::ICameraProvider::castFrom(interface);
+ if (castResult.isOk()) {
+ sp<provider::V2_6::ICameraProvider> interface_2_6 = castResult;
+ if (interface_2_6 != nullptr) {
+ Status callStatus;
+ auto cb =
+ [&isSupported, &callStatus](Status s, bool supported) {
+ callStatus = s;
+ *isSupported = supported; };
+
+ auto ret = interface_2_6->isConcurrentStreamCombinationSupported(
+ halCameraIdsAndStreamCombinations, cb);
+ if (ret.isOk()) {
+ switch (callStatus) {
+ case Status::OK:
+ // Expected case, do nothing.
+ res = OK;
+ break;
+ case Status::METHOD_NOT_SUPPORTED:
+ res = INVALID_OPERATION;
+ break;
+ default:
+ ALOGE("%s: Session configuration query failed: %d", __FUNCTION__,
+ callStatus);
+ res = UNKNOWN_ERROR;
+ }
+ } else {
+ ALOGE("%s: Unexpected binder error: %s", __FUNCTION__, ret.description().c_str());
+ res = UNKNOWN_ERROR;
+ }
+ return res;
+ }
+ }
+ }
+ // unsupported operation
+ return INVALID_OPERATION;
+}
+
template<class DeviceInfoT>
std::unique_ptr<CameraProviderManager::ProviderInfo::DeviceInfo>
CameraProviderManager::ProviderInfo::initializeDeviceInfo(
@@ -2034,6 +2184,18 @@
ALOGE("%s: Unable to derive HEIC tags based on camera and media capabilities: %s (%d)",
__FUNCTION__, strerror(-res), res);
}
+ res = addRotateCropTags();
+ if (OK != res) {
+ ALOGE("%s: Unable to add default SCALER_ROTATE_AND_CROP tags: %s (%d)", __FUNCTION__,
+ strerror(-res), res);
+ }
+
+ res = camera3::ZoomRatioMapper::overrideZoomRatioTags(
+ &mCameraCharacteristics, &mSupportNativeZoomRatio);
+ if (OK != res) {
+ ALOGE("%s: Unable to override zoomRatio related tags: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ }
camera_metadata_entry flashAvailable =
mCameraCharacteristics.find(ANDROID_FLASH_INFO_AVAILABLE);
@@ -2095,6 +2257,13 @@
CameraProviderManager::statusToString(status), status);
return;
}
+
+ res = camera3::ZoomRatioMapper::overrideZoomRatioTags(
+ &mPhysicalCameraCharacteristics[id], &mSupportNativeZoomRatio);
+ if (OK != res) {
+ ALOGE("%s: Unable to override zoomRatio related tags: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ }
}
}
@@ -2572,6 +2741,125 @@
return OK;
}
+// Expects to have mInterfaceMutex locked
+std::vector<std::unordered_set<std::string>>
+CameraProviderManager::getConcurrentStreamingCameraIds() const {
+ std::vector<std::unordered_set<std::string>> deviceIdCombinations;
+ std::lock_guard<std::mutex> lock(mInterfaceMutex);
+ for (auto &provider : mProviders) {
+ for (auto &combinations : provider->mConcurrentCameraIdCombinations) {
+ deviceIdCombinations.push_back(combinations);
+ }
+ }
+ return deviceIdCombinations;
+}
+
+status_t CameraProviderManager::convertToHALStreamCombinationAndCameraIdsLocked(
+ const std::vector<CameraIdAndSessionConfiguration> &cameraIdsAndSessionConfigs,
+ hardware::hidl_vec<CameraIdAndStreamCombination> *halCameraIdsAndStreamCombinations,
+ bool *earlyExit) {
+ binder::Status bStatus = binder::Status::ok();
+ std::vector<CameraIdAndStreamCombination> halCameraIdsAndStreamsV;
+ bool shouldExit = false;
+ status_t res = OK;
+ for (auto &cameraIdAndSessionConfig : cameraIdsAndSessionConfigs) {
+ hardware::camera::device::V3_4::StreamConfiguration streamConfiguration;
+ CameraMetadata deviceInfo;
+ res = getCameraCharacteristicsLocked(cameraIdAndSessionConfig.mCameraId, &deviceInfo);
+ if (res != OK) {
+ return res;
+ }
+ metadataGetter getMetadata =
+ [this](const String8 &id) {
+ CameraMetadata physicalDeviceInfo;
+ getCameraCharacteristicsLocked(id.string(), &physicalDeviceInfo);
+ return physicalDeviceInfo;
+ };
+ std::vector<std::string> physicalCameraIds;
+ isLogicalCameraLocked(cameraIdAndSessionConfig.mCameraId, &physicalCameraIds);
+ bStatus =
+ SessionConfigurationUtils::convertToHALStreamCombination(
+ cameraIdAndSessionConfig.mSessionConfiguration,
+ String8(cameraIdAndSessionConfig.mCameraId.c_str()), deviceInfo, getMetadata,
+ physicalCameraIds, streamConfiguration, &shouldExit);
+ if (!bStatus.isOk()) {
+ ALOGE("%s: convertToHALStreamCombination failed", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ if (shouldExit) {
+ *earlyExit = true;
+ return OK;
+ }
+ CameraIdAndStreamCombination halCameraIdAndStream;
+ halCameraIdAndStream.cameraId = cameraIdAndSessionConfig.mCameraId;
+ halCameraIdAndStream.streamConfiguration = streamConfiguration;
+ halCameraIdsAndStreamsV.push_back(halCameraIdAndStream);
+ }
+ *halCameraIdsAndStreamCombinations = halCameraIdsAndStreamsV;
+ return OK;
+}
+
+// Checks if the containing vector of sets has any set that contains all of the
+// camera ids in cameraIdsAndSessionConfigs.
+static bool checkIfSetContainsAll(
+ const std::vector<CameraIdAndSessionConfiguration> &cameraIdsAndSessionConfigs,
+ const std::vector<std::unordered_set<std::string>> &containingSets) {
+ for (auto &containingSet : containingSets) {
+ bool didHaveAll = true;
+ for (auto &cameraIdAndSessionConfig : cameraIdsAndSessionConfigs) {
+ if (containingSet.find(cameraIdAndSessionConfig.mCameraId) == containingSet.end()) {
+ // a camera id doesn't belong to this set, keep looking in other
+ // sets
+ didHaveAll = false;
+ break;
+ }
+ }
+ if (didHaveAll) {
+ // found a set that has all camera ids, lets return;
+ return true;
+ }
+ }
+ return false;
+}
+
+status_t CameraProviderManager::isConcurrentSessionConfigurationSupported(
+ const std::vector<CameraIdAndSessionConfiguration> &cameraIdsAndSessionConfigs,
+ bool *isSupported) {
+ std::lock_guard<std::mutex> lock(mInterfaceMutex);
+ // Check if all the devices are a subset of devices advertised by the
+ // same provider through getConcurrentStreamingCameraIds()
+ // TODO: we should also do a findDeviceInfoLocked here ?
+ for (auto &provider : mProviders) {
+ if (checkIfSetContainsAll(cameraIdsAndSessionConfigs,
+ provider->mConcurrentCameraIdCombinations)) {
+ // For each camera device in cameraIdsAndSessionConfigs collect
+ // the streamConfigs and create the HAL
+ // CameraIdAndStreamCombination, exit early if needed
+ hardware::hidl_vec<CameraIdAndStreamCombination> halCameraIdsAndStreamCombinations;
+ bool knowUnsupported = false;
+ status_t res = convertToHALStreamCombinationAndCameraIdsLocked(
+ cameraIdsAndSessionConfigs, &halCameraIdsAndStreamCombinations,
+ &knowUnsupported);
+ if (res != OK) {
+ ALOGE("%s unable to convert session configurations provided to HAL stream"
+ "combinations", __FUNCTION__);
+ return res;
+ }
+ if (knowUnsupported) {
+ // We got to know the streams aren't valid before doing the HAL
+ // call itself.
+ *isSupported = false;
+ return OK;
+ }
+ return provider->isConcurrentSessionConfigurationSupported(
+ halCameraIdsAndStreamCombinations, isSupported);
+ }
+ }
+ *isSupported = false;
+ //The set of camera devices were not found
+ return INVALID_OPERATION;
+}
+
status_t CameraProviderManager::getCameraCharacteristicsLocked(const std::string &id,
CameraMetadata* characteristics) const {
auto deviceInfo = findDeviceInfoLocked(id, /*minVersion*/ {3,0}, /*maxVersion*/ {5,0});
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index 2ef1f6f..42da227 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -23,12 +23,15 @@
#include <string>
#include <mutex>
+#include <camera/camera2/ConcurrentCamera.h>
#include <camera/CameraParameters2.h>
#include <camera/CameraMetadata.h>
#include <camera/CameraBase.h>
#include <utils/Errors.h>
#include <android/hardware/camera/common/1.0/types.h>
#include <android/hardware/camera/provider/2.5/ICameraProvider.h>
+#include <android/hardware/camera/provider/2.6/ICameraProviderCallback.h>
+#include <android/hardware/camera/provider/2.6/ICameraProvider.h>
#include <android/hardware/camera/device/3.4/ICameraDeviceSession.h>
#include <android/hidl/manager/1.0/IServiceNotification.h>
#include <camera/VendorTagDescriptor.h>
@@ -136,6 +139,9 @@
virtual void onDeviceStatusChanged(const String8 &cameraId,
hardware::camera::common::V1_0::CameraDeviceStatus newStatus) = 0;
+ virtual void onDeviceStatusChanged(const String8 &cameraId,
+ const String8 &physicalCameraId,
+ hardware::camera::common::V1_0::CameraDeviceStatus newStatus) = 0;
virtual void onTorchStatusChanged(const String8 &cameraId,
hardware::camera::common::V1_0::TorchModeStatus newStatus) = 0;
virtual void onNewProviderRegistered() = 0;
@@ -187,6 +193,11 @@
bool hasFlashUnit(const std::string &id) const;
/**
+ * Return true if the camera device has native zoom ratio support.
+ */
+ bool supportNativeZoomRatio(const std::string &id) const;
+
+ /**
* Return the resource cost of this camera device
*/
status_t getResourceCost(const std::string &id,
@@ -205,6 +216,12 @@
status_t getCameraCharacteristics(const std::string &id,
CameraMetadata* characteristics) const;
+ status_t isConcurrentSessionConfigurationSupported(
+ const std::vector<hardware::camera2::utils::CameraIdAndSessionConfiguration>
+ &cameraIdsAndSessionConfigs,
+ bool *isSupported);
+
+ std::vector<std::unordered_set<std::string>> getConcurrentStreamingCameraIds() const;
/**
* Check for device support of specific stream combination.
*/
@@ -337,7 +354,7 @@
std::mutex mProviderInterfaceMapLock;
struct ProviderInfo :
- virtual public hardware::camera::provider::V2_4::ICameraProviderCallback,
+ virtual public hardware::camera::provider::V2_6::ICameraProviderCallback,
virtual public hardware::hidl_death_recipient
{
const std::string mProviderName;
@@ -375,12 +392,16 @@
status_t dump(int fd, const Vector<String16>& args) const;
// ICameraProviderCallbacks interface - these lock the parent mInterfaceMutex
- virtual hardware::Return<void> cameraDeviceStatusChange(
+ hardware::Return<void> cameraDeviceStatusChange(
const hardware::hidl_string& cameraDeviceName,
hardware::camera::common::V1_0::CameraDeviceStatus newStatus) override;
- virtual hardware::Return<void> torchModeStatusChange(
+ hardware::Return<void> torchModeStatusChange(
const hardware::hidl_string& cameraDeviceName,
hardware::camera::common::V1_0::TorchModeStatus newStatus) override;
+ hardware::Return<void> physicalCameraDeviceStatusChange(
+ const hardware::hidl_string& cameraDeviceName,
+ const hardware::hidl_string& physicalCameraDeviceName,
+ hardware::camera::common::V1_0::CameraDeviceStatus newStatus) override;
// hidl_death_recipient interface - this locks the parent mInterfaceMutex
virtual void serviceDied(uint64_t cookie, const wp<hidl::base::V1_0::IBase>& who) override;
@@ -396,6 +417,14 @@
status_t notifyDeviceStateChange(
hardware::hidl_bitfield<hardware::camera::provider::V2_5::DeviceState>
newDeviceState);
+ /**
+ * Query the camera provider for concurrent stream configuration support
+ */
+ status_t isConcurrentSessionConfigurationSupported(
+ const hardware::hidl_vec<
+ hardware::camera::provider::V2_6::CameraIdAndStreamCombination>
+ &halCameraIdsAndStreamCombinations,
+ bool *isSupported);
// Basic device information, common to all camera devices
struct DeviceInfo {
@@ -412,10 +441,13 @@
const hardware::camera::common::V1_0::CameraResourceCost mResourceCost;
hardware::camera::common::V1_0::CameraDeviceStatus mStatus;
+ std::map<std::string, hardware::camera::common::V1_0::CameraDeviceStatus>
+ mPhysicalStatus;
sp<ProviderInfo> mParentProvider;
bool hasFlashUnit() const { return mHasFlashUnit; }
+ bool supportNativeZoomRatio() const { return mSupportNativeZoomRatio; }
virtual status_t setTorchMode(bool enabled) = 0;
virtual status_t getCameraInfo(hardware::CameraInfo *info) const = 0;
virtual bool isAPI1Compatible() const = 0;
@@ -449,10 +481,11 @@
mIsLogicalCamera(false), mResourceCost(resourceCost),
mStatus(hardware::camera::common::V1_0::CameraDeviceStatus::PRESENT),
mParentProvider(parentProvider), mHasFlashUnit(false),
- mPublicCameraIds(publicCameraIds) {}
+ mSupportNativeZoomRatio(false), mPublicCameraIds(publicCameraIds) {}
virtual ~DeviceInfo();
protected:
- bool mHasFlashUnit;
+ bool mHasFlashUnit; // const after constructor
+ bool mSupportNativeZoomRatio; // const after constructor
const std::vector<std::string>& mPublicCameraIds;
template<class InterfaceT>
@@ -477,6 +510,8 @@
// physical camera IDs.
std::vector<std::string> mProviderPublicCameraIds;
+ std::vector<std::unordered_set<std::string>> mConcurrentCameraIdCombinations;
+
// HALv1-specific camera fields, including the actual device interface
struct DeviceInfo1 : public DeviceInfo {
typedef hardware::camera::device::V1_0::ICameraDevice InterfaceT;
@@ -528,6 +563,9 @@
SystemCameraKind getSystemCameraKind();
status_t fixupMonochromeTags();
status_t addDynamicDepthTags();
+ status_t deriveHeicTags();
+ status_t addRotateCropTags();
+
static void getSupportedSizes(const CameraMetadata& ch, uint32_t tag,
android_pixel_format_t format,
std::vector<std::tuple<size_t, size_t>> *sizes /*out*/);
@@ -538,7 +576,6 @@
void getSupportedDynamicDepthDurations(const std::vector<int64_t>& depthDurations,
const std::vector<int64_t>& blobDurations,
std::vector<int64_t> *dynamicDepthDurations /*out*/);
- static bool isDepthPhotoLibraryPresent();
static void getSupportedDynamicDepthSizes(
const std::vector<std::tuple<size_t, size_t>>& blobSizes,
const std::vector<std::tuple<size_t, size_t>>& depthSizes,
@@ -551,7 +588,6 @@
std::vector<int64_t>* stallDurations,
const camera_metadata_entry& halStreamConfigs,
const camera_metadata_entry& halStreamDurations);
- status_t deriveHeicTags();
};
private:
@@ -600,6 +636,8 @@
status_t addProviderLocked(const std::string& newProvider);
+ bool isLogicalCameraLocked(const std::string& id, std::vector<std::string>* physicalCameraIds);
+
status_t removeProvider(const std::string& provider);
sp<StatusListener> getStatusListener() const;
@@ -630,6 +668,13 @@
void collectDeviceIdsLocked(const std::vector<std::string> deviceIds,
std::vector<std::string>& normalDeviceIds,
std::vector<std::string>& systemCameraDeviceIds) const;
+
+ status_t convertToHALStreamCombinationAndCameraIdsLocked(
+ const std::vector<hardware::camera2::utils::CameraIdAndSessionConfiguration>
+ &cameraIdsAndSessionConfigs,
+ hardware::hidl_vec<hardware::camera::provider::V2_6::CameraIdAndStreamCombination>
+ *halCameraIdsAndStreamCombinations,
+ bool *earlyExit);
};
} // namespace android
diff --git a/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp b/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp
index 94541d8..c995670 100644
--- a/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp
+++ b/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp
@@ -410,7 +410,7 @@
return DepthMap::FromData(depthParams, items);
}
-extern "C" int processDepthPhotoFrame(DepthPhotoInputFrame inputFrame, size_t depthPhotoBufferSize,
+int processDepthPhotoFrame(DepthPhotoInputFrame inputFrame, size_t depthPhotoBufferSize,
void* depthPhotoBuffer /*out*/, size_t* depthPhotoActualSize /*out*/) {
if ((inputFrame.mMainJpegBuffer == nullptr) || (inputFrame.mDepthMapBuffer == nullptr) ||
(depthPhotoBuffer == nullptr) || (depthPhotoActualSize == nullptr)) {
diff --git a/services/camera/libcameraservice/common/DepthPhotoProcessor.h b/services/camera/libcameraservice/common/DepthPhotoProcessor.h
index ba5ca9e..09b6935 100644
--- a/services/camera/libcameraservice/common/DepthPhotoProcessor.h
+++ b/services/camera/libcameraservice/common/DepthPhotoProcessor.h
@@ -64,9 +64,7 @@
mOrientation(DepthPhotoOrientation::DEPTH_ORIENTATION_0_DEGREES) {}
};
-static const char *kDepthPhotoLibrary = "libdepthphoto.so";
-static const char *kDepthPhotoProcessFunction = "processDepthPhotoFrame";
-typedef int (*process_depth_photo_frame) (DepthPhotoInputFrame /*inputFrame*/,
+int processDepthPhotoFrame(DepthPhotoInputFrame /*inputFrame*/,
size_t /*depthPhotoBufferSize*/, void* /*depthPhotoBuffer out*/,
size_t* /*depthPhotoActualSize out*/);
diff --git a/services/camera/libcameraservice/common/FrameProcessorBase.cpp b/services/camera/libcameraservice/common/FrameProcessorBase.cpp
index 3d56cd2..e259379 100644
--- a/services/camera/libcameraservice/common/FrameProcessorBase.cpp
+++ b/services/camera/libcameraservice/common/FrameProcessorBase.cpp
@@ -18,20 +18,21 @@
#define ATRACE_TAG ATRACE_TAG_CAMERA
//#define LOG_NDEBUG 0
+#include <map>
#include <utils/Log.h>
#include <utils/Trace.h>
+#include "common/FrameProducer.h"
#include "common/FrameProcessorBase.h"
-#include "common/CameraDeviceBase.h"
namespace android {
namespace camera2 {
-FrameProcessorBase::FrameProcessorBase(wp<CameraDeviceBase> device) :
+FrameProcessorBase::FrameProcessorBase(wp<FrameProducer> device) :
Thread(/*canCallJava*/false),
mDevice(device),
mNumPartialResults(1) {
- sp<CameraDeviceBase> cameraDevice = device.promote();
+ sp<FrameProducer> cameraDevice = device.promote();
if (cameraDevice != 0) {
CameraMetadata staticInfo = cameraDevice->info();
camera_metadata_entry_t entry = staticInfo.find(ANDROID_REQUEST_PARTIAL_RESULT_COUNT);
@@ -115,7 +116,7 @@
bool FrameProcessorBase::threadLoop() {
status_t res;
- sp<CameraDeviceBase> device;
+ sp<FrameProducer> device;
{
device = mDevice.promote();
if (device == 0) return false;
@@ -132,7 +133,7 @@
return true;
}
-void FrameProcessorBase::processNewFrames(const sp<CameraDeviceBase> &device) {
+void FrameProcessorBase::processNewFrames(const sp<FrameProducer> &device) {
status_t res;
ATRACE_CALL();
CaptureResult result;
@@ -142,7 +143,7 @@
while ( (res = device->getNextResult(&result)) == OK) {
// TODO: instead of getting frame number from metadata, we should read
- // this from result.mResultExtras when CameraDeviceBase interface is fixed.
+ // this from result.mResultExtras when FrameProducer interface is fixed.
camera_metadata_entry_t entry;
entry = result.mMetadata.find(ANDROID_REQUEST_FRAME_COUNT);
@@ -174,14 +175,14 @@
}
bool FrameProcessorBase::processSingleFrame(CaptureResult &result,
- const sp<CameraDeviceBase> &device) {
+ const sp<FrameProducer> &device) {
ALOGV("%s: Camera %s: Process single frame (is empty? %d)",
__FUNCTION__, device->getId().string(), result.mMetadata.isEmpty());
return processListeners(result, device) == OK;
}
status_t FrameProcessorBase::processListeners(const CaptureResult &result,
- const sp<CameraDeviceBase> &device) {
+ const sp<FrameProducer> &device) {
ATRACE_CALL();
camera_metadata_ro_entry_t entry;
diff --git a/services/camera/libcameraservice/common/FrameProcessorBase.h b/services/camera/libcameraservice/common/FrameProcessorBase.h
index ae6d15d..be1ebc6 100644
--- a/services/camera/libcameraservice/common/FrameProcessorBase.h
+++ b/services/camera/libcameraservice/common/FrameProcessorBase.h
@@ -27,22 +27,25 @@
namespace android {
-class CameraDeviceBase;
+class FrameProducer;
namespace camera2 {
/* Output frame metadata processing thread. This thread waits for new
- * frames from the device, and analyzes them as necessary.
+ * frames from the frame producer, and analyzes them as necessary.
*/
class FrameProcessorBase: public Thread {
public:
- explicit FrameProcessorBase(wp<CameraDeviceBase> device);
+ explicit FrameProcessorBase(wp<FrameProducer> device);
virtual ~FrameProcessorBase();
struct FilteredListener: virtual public RefBase {
virtual void onResultAvailable(const CaptureResult &result) = 0;
};
+ static const int32_t FRAME_PROCESSOR_LISTENER_MIN_ID = 0;
+ static const int32_t FRAME_PROCESSOR_LISTENER_MAX_ID = 0x7fffffffL;
+
// Register a listener for a range of IDs [minId, maxId). Multiple listeners
// can be listening to the same range. Registering the same listener with
// the same range of IDs has no effect.
@@ -56,7 +59,7 @@
void dump(int fd, const Vector<String16>& args);
protected:
static const nsecs_t kWaitDuration = 10000000; // 10 ms
- wp<CameraDeviceBase> mDevice;
+ wp<FrameProducer> mDevice;
virtual bool threadLoop();
@@ -74,13 +77,13 @@
// Number of partial result the HAL will potentially send.
int32_t mNumPartialResults;
- void processNewFrames(const sp<CameraDeviceBase> &device);
+ void processNewFrames(const sp<FrameProducer> &device);
virtual bool processSingleFrame(CaptureResult &result,
- const sp<CameraDeviceBase> &device);
+ const sp<FrameProducer> &device);
status_t processListeners(const CaptureResult &result,
- const sp<CameraDeviceBase> &device);
+ const sp<FrameProducer> &device);
CameraMetadata mLastFrame;
std::vector<PhysicalCaptureResultInfo> mLastPhysicalFrames;
diff --git a/services/camera/libcameraservice/common/FrameProducer.h b/services/camera/libcameraservice/common/FrameProducer.h
new file mode 100644
index 0000000..a14b3d6
--- /dev/null
+++ b/services/camera/libcameraservice/common/FrameProducer.h
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_SERVERS_CAMERA_FRAMEPRODUCER_H
+#define ANDROID_SERVERS_CAMERA_FRAMEPRODUCER_H
+
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <utils/Timers.h>
+
+#include "camera/CameraMetadata.h"
+#include "camera/CaptureResult.h"
+
+namespace android {
+
+/**
+ * Abstract class for HAL frame producers
+ */
+class FrameProducer : public virtual RefBase {
+ public:
+ /**
+ * Retrieve the static characteristics metadata buffer
+ */
+ virtual const CameraMetadata& info() const = 0;
+
+ /**
+ * Retrieve the device camera ID
+ */
+ virtual const String8& getId() const = 0;
+
+ /**
+ * Wait for a new frame to be produced, with timeout in nanoseconds.
+ * Returns TIMED_OUT when no frame produced within the specified duration
+ * May be called concurrently to most methods, except for getNextFrame
+ */
+ virtual status_t waitForNextFrame(nsecs_t timeout) = 0;
+
+ /**
+ * Get next capture result frame from the result queue. Returns NOT_ENOUGH_DATA
+ * if the queue is empty; caller takes ownership of the metadata buffer inside
+ * the capture result object's metadata field.
+ * May be called concurrently to most methods, except for waitForNextFrame.
+ */
+ virtual status_t getNextResult(CaptureResult *frame) = 0;
+
+}; // class FrameProducer
+
+} // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/device3/BufferUtils.cpp b/services/camera/libcameraservice/device3/BufferUtils.cpp
new file mode 100644
index 0000000..cc29390
--- /dev/null
+++ b/services/camera/libcameraservice/device3/BufferUtils.cpp
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Camera3-BufUtils"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+//#define LOG_NNDEBUG 0 // Per-frame verbose logging
+
+#include <inttypes.h>
+
+#include <utils/Log.h>
+
+#include "device3/BufferUtils.h"
+
+namespace android {
+namespace camera3 {
+
+camera3_buffer_status_t mapHidlBufferStatus(hardware::camera::device::V3_2::BufferStatus status) {
+ using hardware::camera::device::V3_2::BufferStatus;
+
+ switch (status) {
+ case BufferStatus::OK: return CAMERA3_BUFFER_STATUS_OK;
+ case BufferStatus::ERROR: return CAMERA3_BUFFER_STATUS_ERROR;
+ }
+ return CAMERA3_BUFFER_STATUS_ERROR;
+}
+
+void BufferRecords::takeInflightBufferMap(BufferRecords& other) {
+ std::lock_guard<std::mutex> oLock(other.mInflightLock);
+ std::lock_guard<std::mutex> lock(mInflightLock);
+ if (mInflightBufferMap.size() > 0) {
+ ALOGE("%s: inflight map is set in non-empty state!", __FUNCTION__);
+ }
+ mInflightBufferMap = std::move(other.mInflightBufferMap);
+ other.mInflightBufferMap.clear();
+}
+
+void BufferRecords::takeRequestedBufferMap(BufferRecords& other) {
+ std::lock_guard<std::mutex> oLock(other.mRequestedBuffersLock);
+ std::lock_guard<std::mutex> lock(mRequestedBuffersLock);
+ if (mRequestedBufferMap.size() > 0) {
+ ALOGE("%s: requested buffer map is set in non-empty state!", __FUNCTION__);
+ }
+ mRequestedBufferMap = std::move(other.mRequestedBufferMap);
+ other.mRequestedBufferMap.clear();
+}
+
+void BufferRecords::takeBufferCaches(BufferRecords& other, const std::vector<int32_t>& streams) {
+ std::lock_guard<std::mutex> oLock(other.mBufferIdMapLock);
+ std::lock_guard<std::mutex> lock(mBufferIdMapLock);
+ if (mBufferIdMaps.size() > 0) {
+ ALOGE("%s: buffer ID map is set in non-empty state!", __FUNCTION__);
+ }
+ for (auto streamId : streams) {
+ mBufferIdMaps.insert({streamId, std::move(other.mBufferIdMaps.at(streamId))});
+ }
+ other.mBufferIdMaps.clear();
+}
+
+std::pair<bool, uint64_t> BufferRecords::getBufferId(
+ const buffer_handle_t& buf, int streamId) {
+ std::lock_guard<std::mutex> lock(mBufferIdMapLock);
+
+ BufferIdMap& bIdMap = mBufferIdMaps.at(streamId);
+ auto it = bIdMap.find(buf);
+ if (it == bIdMap.end()) {
+ bIdMap[buf] = mNextBufferId++;
+ ALOGV("stream %d now have %zu buffer caches, buf %p",
+ streamId, bIdMap.size(), buf);
+ return std::make_pair(true, mNextBufferId - 1);
+ } else {
+ return std::make_pair(false, it->second);
+ }
+}
+
+void BufferRecords::tryCreateBufferCache(int streamId) {
+ std::lock_guard<std::mutex> lock(mBufferIdMapLock);
+ if (mBufferIdMaps.count(streamId) == 0) {
+ mBufferIdMaps.emplace(streamId, BufferIdMap{});
+ }
+}
+
+void BufferRecords::removeInactiveBufferCaches(const std::set<int32_t>& activeStreams) {
+ std::lock_guard<std::mutex> lock(mBufferIdMapLock);
+ for(auto it = mBufferIdMaps.begin(); it != mBufferIdMaps.end();) {
+ int streamId = it->first;
+ bool active = activeStreams.count(streamId) > 0;
+ if (!active) {
+ it = mBufferIdMaps.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+uint64_t BufferRecords::removeOneBufferCache(int streamId, const native_handle_t* handle) {
+ std::lock_guard<std::mutex> lock(mBufferIdMapLock);
+ uint64_t bufferId = BUFFER_ID_NO_BUFFER;
+ auto mapIt = mBufferIdMaps.find(streamId);
+ if (mapIt == mBufferIdMaps.end()) {
+ // streamId might be from a deleted stream here
+ ALOGI("%s: stream %d has been removed",
+ __FUNCTION__, streamId);
+ return BUFFER_ID_NO_BUFFER;
+ }
+ BufferIdMap& bIdMap = mapIt->second;
+ auto it = bIdMap.find(handle);
+ if (it == bIdMap.end()) {
+ ALOGW("%s: cannot find buffer %p in stream %d",
+ __FUNCTION__, handle, streamId);
+ return BUFFER_ID_NO_BUFFER;
+ } else {
+ bufferId = it->second;
+ bIdMap.erase(it);
+ ALOGV("%s: stream %d now have %zu buffer caches after removing buf %p",
+ __FUNCTION__, streamId, bIdMap.size(), handle);
+ }
+ return bufferId;
+}
+
+std::vector<uint64_t> BufferRecords::clearBufferCaches(int streamId) {
+ std::lock_guard<std::mutex> lock(mBufferIdMapLock);
+ std::vector<uint64_t> ret;
+ auto mapIt = mBufferIdMaps.find(streamId);
+ if (mapIt == mBufferIdMaps.end()) {
+ ALOGE("%s: streamId %d not found!", __FUNCTION__, streamId);
+ return ret;
+ }
+ BufferIdMap& bIdMap = mapIt->second;
+ ret.reserve(bIdMap.size());
+ for (const auto& it : bIdMap) {
+ ret.push_back(it.second);
+ }
+ bIdMap.clear();
+ return ret;
+}
+
+bool BufferRecords::isStreamCached(int streamId) {
+ std::lock_guard<std::mutex> lock(mBufferIdMapLock);
+ return mBufferIdMaps.find(streamId) != mBufferIdMaps.end();
+}
+
+bool BufferRecords::verifyBufferIds(
+ int32_t streamId, std::vector<uint64_t>& bufIds) {
+ std::lock_guard<std::mutex> lock(mBufferIdMapLock);
+ camera3::BufferIdMap& bIdMap = mBufferIdMaps.at(streamId);
+ if (bIdMap.size() != bufIds.size()) {
+ ALOGE("%s: stream ID %d buffer cache number mismatch: %zu/%zu (service/HAL)",
+ __FUNCTION__, streamId, bIdMap.size(), bufIds.size());
+ return false;
+ }
+ std::vector<uint64_t> internalBufIds;
+ internalBufIds.reserve(bIdMap.size());
+ for (const auto& pair : bIdMap) {
+ internalBufIds.push_back(pair.second);
+ }
+ std::sort(bufIds.begin(), bufIds.end());
+ std::sort(internalBufIds.begin(), internalBufIds.end());
+ for (size_t i = 0; i < bufIds.size(); i++) {
+ if (bufIds[i] != internalBufIds[i]) {
+ ALOGE("%s: buffer cache mismatch! Service %" PRIu64 ", HAL %" PRIu64,
+ __FUNCTION__, internalBufIds[i], bufIds[i]);
+ return false;
+ }
+ }
+ return true;
+}
+
+void BufferRecords::getInflightBufferKeys(
+ std::vector<std::pair<int32_t, int32_t>>* out) {
+ std::lock_guard<std::mutex> lock(mInflightLock);
+ out->clear();
+ out->reserve(mInflightBufferMap.size());
+ for (auto& pair : mInflightBufferMap) {
+ uint64_t key = pair.first;
+ int32_t streamId = key & 0xFFFFFFFF;
+ int32_t frameNumber = (key >> 32) & 0xFFFFFFFF;
+ out->push_back(std::make_pair(frameNumber, streamId));
+ }
+ return;
+}
+
+status_t BufferRecords::pushInflightBuffer(
+ int32_t frameNumber, int32_t streamId, buffer_handle_t *buffer) {
+ std::lock_guard<std::mutex> lock(mInflightLock);
+ uint64_t key = static_cast<uint64_t>(frameNumber) << 32 | static_cast<uint64_t>(streamId);
+ mInflightBufferMap[key] = buffer;
+ return OK;
+}
+
+status_t BufferRecords::popInflightBuffer(
+ int32_t frameNumber, int32_t streamId,
+ /*out*/ buffer_handle_t **buffer) {
+ std::lock_guard<std::mutex> lock(mInflightLock);
+
+ uint64_t key = static_cast<uint64_t>(frameNumber) << 32 | static_cast<uint64_t>(streamId);
+ auto it = mInflightBufferMap.find(key);
+ if (it == mInflightBufferMap.end()) return NAME_NOT_FOUND;
+ if (buffer != nullptr) {
+ *buffer = it->second;
+ }
+ mInflightBufferMap.erase(it);
+ return OK;
+}
+
+void BufferRecords::popInflightBuffers(
+ const std::vector<std::pair<int32_t, int32_t>>& buffers) {
+ for (const auto& pair : buffers) {
+ int32_t frameNumber = pair.first;
+ int32_t streamId = pair.second;
+ popInflightBuffer(frameNumber, streamId, nullptr);
+ }
+}
+
+status_t BufferRecords::pushInflightRequestBuffer(
+ uint64_t bufferId, buffer_handle_t* buf, int32_t streamId) {
+ std::lock_guard<std::mutex> lock(mRequestedBuffersLock);
+ auto pair = mRequestedBufferMap.insert({bufferId, {streamId, buf}});
+ if (!pair.second) {
+ ALOGE("%s: bufId %" PRIu64 " is already inflight!",
+ __FUNCTION__, bufferId);
+ return BAD_VALUE;
+ }
+ return OK;
+}
+
+// Find and pop a buffer_handle_t based on bufferId
+status_t BufferRecords::popInflightRequestBuffer(
+ uint64_t bufferId,
+ /*out*/ buffer_handle_t** buffer,
+ /*optional out*/ int32_t* streamId) {
+ if (buffer == nullptr) {
+ ALOGE("%s: buffer (%p) must not be null", __FUNCTION__, buffer);
+ return BAD_VALUE;
+ }
+ std::lock_guard<std::mutex> lock(mRequestedBuffersLock);
+ auto it = mRequestedBufferMap.find(bufferId);
+ if (it == mRequestedBufferMap.end()) {
+ ALOGE("%s: bufId %" PRIu64 " is not inflight!",
+ __FUNCTION__, bufferId);
+ return BAD_VALUE;
+ }
+ *buffer = it->second.second;
+ if (streamId != nullptr) {
+ *streamId = it->second.first;
+ }
+ mRequestedBufferMap.erase(it);
+ return OK;
+}
+
+void BufferRecords::getInflightRequestBufferKeys(
+ std::vector<uint64_t>* out) {
+ std::lock_guard<std::mutex> lock(mRequestedBuffersLock);
+ out->clear();
+ out->reserve(mRequestedBufferMap.size());
+ for (auto& pair : mRequestedBufferMap) {
+ out->push_back(pair.first);
+ }
+ return;
+}
+
+
+} // camera3
+} // namespace android
diff --git a/services/camera/libcameraservice/device3/BufferUtils.h b/services/camera/libcameraservice/device3/BufferUtils.h
new file mode 100644
index 0000000..452a908
--- /dev/null
+++ b/services/camera/libcameraservice/device3/BufferUtils.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SERVERS_CAMERA3_BUFFER_UTILS_H
+#define ANDROID_SERVERS_CAMERA3_BUFFER_UTILS_H
+
+#include <unordered_map>
+#include <mutex>
+#include <set>
+
+#include <cutils/native_handle.h>
+
+#include <android/hardware/camera/device/3.2/ICameraDevice.h>
+
+// TODO: remove legacy camera3.h references
+#include "hardware/camera3.h"
+
+#include <device3/Camera3OutputInterface.h>
+
+namespace android {
+
+namespace camera3 {
+
+ struct BufferHasher {
+ size_t operator()(const buffer_handle_t& buf) const {
+ if (buf == nullptr)
+ return 0;
+
+ size_t result = 1;
+ result = 31 * result + buf->numFds;
+ for (int i = 0; i < buf->numFds; i++) {
+ result = 31 * result + buf->data[i];
+ }
+ return result;
+ }
+ };
+
+ struct BufferComparator {
+ bool operator()(const buffer_handle_t& buf1, const buffer_handle_t& buf2) const {
+ if (buf1->numFds == buf2->numFds) {
+ for (int i = 0; i < buf1->numFds; i++) {
+ if (buf1->data[i] != buf2->data[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+ };
+
+ // Per stream buffer native handle -> bufId map
+ typedef std::unordered_map<const buffer_handle_t, uint64_t,
+ BufferHasher, BufferComparator> BufferIdMap;
+
+ // streamId -> BufferIdMap
+ typedef std::unordered_map<int, BufferIdMap> BufferIdMaps;
+
+ // Map of inflight buffers sent along in capture requests.
+ // Key is composed by (frameNumber << 32 | streamId)
+ typedef std::unordered_map<uint64_t, buffer_handle_t*> InflightBufferMap;
+
+ // Map of inflight buffers dealt by requestStreamBuffers API
+ typedef std::unordered_map<uint64_t, std::pair<int32_t, buffer_handle_t*>> RequestedBufferMap;
+
+ // A struct containing all buffer tracking information like inflight buffers
+ // and buffer ID caches
+ class BufferRecords : public BufferRecordsInterface {
+
+ public:
+ BufferRecords() {}
+
+ BufferRecords(BufferRecords&& other) :
+ mBufferIdMaps(other.mBufferIdMaps),
+ mNextBufferId(other.mNextBufferId),
+ mInflightBufferMap(other.mInflightBufferMap),
+ mRequestedBufferMap(other.mRequestedBufferMap) {}
+
+ virtual ~BufferRecords() {}
+
+ // Helper methods to help moving buffer records
+ void takeInflightBufferMap(BufferRecords& other);
+ void takeRequestedBufferMap(BufferRecords& other);
+ void takeBufferCaches(BufferRecords& other, const std::vector<int32_t>& streams);
+
+ // method to extract buffer's unique ID
+ // return pair of (newlySeenBuffer?, bufferId)
+ virtual std::pair<bool, uint64_t> getBufferId(
+ const buffer_handle_t& buf, int streamId) override;
+
+ void tryCreateBufferCache(int streamId);
+
+ void removeInactiveBufferCaches(const std::set<int32_t>& activeStreams);
+
+ // Return the removed buffer ID if input cache is found.
+ // Otherwise return BUFFER_ID_NO_BUFFER
+ uint64_t removeOneBufferCache(int streamId, const native_handle_t* handle);
+
+ // Clear all caches for input stream, but do not remove the stream
+ // Removed buffers' ID are returned
+ std::vector<uint64_t> clearBufferCaches(int streamId);
+
+ bool isStreamCached(int streamId);
+
+ // Return true if the input caches match what we have; otherwise false
+ bool verifyBufferIds(int32_t streamId, std::vector<uint64_t>& inBufIds);
+
+ // Get a vector of (frameNumber, streamId) pair of currently inflight
+ // buffers
+ void getInflightBufferKeys(std::vector<std::pair<int32_t, int32_t>>* out);
+
+ status_t pushInflightBuffer(int32_t frameNumber, int32_t streamId,
+ buffer_handle_t *buffer);
+
+ // Find a buffer_handle_t based on frame number and stream ID
+ virtual status_t popInflightBuffer(int32_t frameNumber, int32_t streamId,
+ /*out*/ buffer_handle_t **buffer) override;
+
+ // Pop inflight buffers based on pairs of (frameNumber,streamId)
+ void popInflightBuffers(const std::vector<std::pair<int32_t, int32_t>>& buffers);
+
+ // Get a vector of bufferId of currently inflight buffers
+ void getInflightRequestBufferKeys(std::vector<uint64_t>* out);
+
+ // Register a bufId (streamId, buffer_handle_t) to inflight request buffer
+ virtual status_t pushInflightRequestBuffer(
+ uint64_t bufferId, buffer_handle_t* buf, int32_t streamId) override;
+
+ // Find a buffer_handle_t based on bufferId
+ virtual status_t popInflightRequestBuffer(uint64_t bufferId,
+ /*out*/ buffer_handle_t** buffer,
+ /*optional out*/ int32_t* streamId = nullptr) override;
+
+ private:
+ std::mutex mBufferIdMapLock;
+ BufferIdMaps mBufferIdMaps;
+ uint64_t mNextBufferId = 1; // 0 means no buffer
+
+ std::mutex mInflightLock;
+ InflightBufferMap mInflightBufferMap;
+
+ std::mutex mRequestedBuffersLock;
+ RequestedBufferMap mRequestedBufferMap;
+ }; // class BufferRecords
+
+ static const uint64_t BUFFER_ID_NO_BUFFER = 0;
+
+ camera3_buffer_status_t mapHidlBufferStatus(
+ hardware::camera::device::V3_2::BufferStatus status);
+} // namespace camera3
+
+} // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 2541365..90d21a2 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -61,6 +61,7 @@
#include "CameraService.h"
#include "utils/CameraThreadState.h"
+#include <algorithm>
#include <tuple>
using namespace android::camera3;
@@ -132,6 +133,7 @@
session->close();
return res;
}
+ mSupportNativeZoomRatio = manager->supportNativeZoomRatio(mId.string());
std::vector<std::string> physicalCameraIds;
bool isLogical = manager->isLogicalCamera(mId.string(), &physicalCameraIds);
@@ -146,8 +148,11 @@
return res;
}
- if (DistortionMapper::isDistortionSupported(mPhysicalDeviceInfoMap[physicalId])) {
- mDistortionMappers[physicalId].setupStaticInfo(mPhysicalDeviceInfoMap[physicalId]);
+ bool usePrecorrectArray =
+ DistortionMapper::isDistortionSupported(mPhysicalDeviceInfoMap[physicalId]);
+ if (usePrecorrectArray) {
+ res = mDistortionMappers[physicalId].setupStaticInfo(
+ mPhysicalDeviceInfoMap[physicalId]);
if (res != OK) {
SET_ERR_L("Unable to read camera %s's calibration fields for distortion "
"correction", physicalId.c_str());
@@ -155,6 +160,10 @@
return res;
}
}
+
+ mZoomRatioMappers[physicalId] = ZoomRatioMapper(
+ &mPhysicalDeviceInfoMap[physicalId],
+ mSupportNativeZoomRatio, usePrecorrectArray);
}
}
@@ -206,7 +215,15 @@
ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_HIDL_DEVICE_3_5);
}
- mInterface = new HalInterface(session, queue, mUseHalBufManager);
+ camera_metadata_entry_t capabilities = mDeviceInfo.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
+ for (size_t i = 0; i < capabilities.count; i++) {
+ uint8_t capability = capabilities.data.u8[i];
+ if (capability == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_OFFLINE_PROCESSING) {
+ mSupportOfflineProcessing = true;
+ }
+ }
+
+ mInterface = new HalInterface(session, queue, mUseHalBufManager, mSupportOfflineProcessing);
std::string providerType;
mVendorTagId = manager->getProviderTagIdLocked(mId.string());
mTagMonitor.initialize(mVendorTagId);
@@ -227,9 +244,8 @@
maxVersion.get_major(), maxVersion.get_minor());
bool isMonochrome = false;
- camera_metadata_entry_t entry = mDeviceInfo.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
- for (size_t i = 0; i < entry.count; i++) {
- uint8_t capability = entry.data.u8[i];
+ for (size_t i = 0; i < capabilities.count; i++) {
+ uint8_t capability = capabilities.data.u8[i];
if (capability == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME) {
isMonochrome = true;
}
@@ -323,13 +339,22 @@
}
}
- if (DistortionMapper::isDistortionSupported(mDeviceInfo)) {
+ bool usePrecorrectArray = DistortionMapper::isDistortionSupported(mDeviceInfo);
+ if (usePrecorrectArray) {
res = mDistortionMappers[mId.c_str()].setupStaticInfo(mDeviceInfo);
if (res != OK) {
SET_ERR_L("Unable to read necessary calibration fields for distortion correction");
return res;
}
}
+
+ mZoomRatioMappers[mId.c_str()] = ZoomRatioMapper(&mDeviceInfo,
+ mSupportNativeZoomRatio, usePrecorrectArray);
+
+ if (RotateAndCropMapper::isNeeded(&mDeviceInfo)) {
+ mRotateAndCropMappers.emplace(mId.c_str(), &mDeviceInfo);
+ }
+
return OK;
}
@@ -553,14 +578,6 @@
return OK;
}
-camera3_buffer_status_t Camera3Device::mapHidlBufferStatus(BufferStatus status) {
- switch (status) {
- case BufferStatus::OK: return CAMERA3_BUFFER_STATUS_OK;
- case BufferStatus::ERROR: return CAMERA3_BUFFER_STATUS_ERROR;
- }
- return CAMERA3_BUFFER_STATUS_ERROR;
-}
-
int Camera3Device::mapToFrameworkFormat(
hardware::graphics::common::V1_0::PixelFormat pixelFormat) {
return static_cast<uint32_t>(pixelFormat);
@@ -797,7 +814,7 @@
return OK;
}
-const CameraMetadata& Camera3Device::info(const String8& physicalId) const {
+const CameraMetadata& Camera3Device::infoPhysical(const String8& physicalId) const {
ALOGVV("%s: E", __FUNCTION__);
if (CC_UNLIKELY(mStatus == STATUS_UNINITIALIZED ||
mStatus == STATUS_ERROR)) {
@@ -820,7 +837,7 @@
const CameraMetadata& Camera3Device::info() const {
String8 emptyId;
- return info(emptyId);
+ return infoPhysical(emptyId);
}
status_t Camera3Device::checkStatusOkToCaptureLocked() {
@@ -868,17 +885,12 @@
// Setup burst Id and request Id
newRequest->mResultExtras.burstId = burstId++;
- if (metadataIt->begin()->metadata.exists(ANDROID_REQUEST_ID)) {
- if (metadataIt->begin()->metadata.find(ANDROID_REQUEST_ID).count == 0) {
- CLOGE("RequestID entry exists; but must not be empty in metadata");
- return BAD_VALUE;
- }
- newRequest->mResultExtras.requestId = metadataIt->begin()->metadata.find(
- ANDROID_REQUEST_ID).data.i32[0];
- } else {
+ auto requestIdEntry = metadataIt->begin()->metadata.find(ANDROID_REQUEST_ID);
+ if (requestIdEntry.count == 0) {
CLOGE("RequestID does not exist in metadata");
return BAD_VALUE;
}
+ newRequest->mResultExtras.requestId = requestIdEntry.data.i32[0];
requestList->push_back(newRequest);
@@ -980,230 +992,18 @@
hardware::Return<void> Camera3Device::requestStreamBuffers(
const hardware::hidl_vec<hardware::camera::device::V3_5::BufferRequest>& bufReqs,
requestStreamBuffers_cb _hidl_cb) {
- using hardware::camera::device::V3_5::BufferRequestStatus;
- using hardware::camera::device::V3_5::StreamBufferRet;
- using hardware::camera::device::V3_5::StreamBufferRequestError;
-
- std::lock_guard<std::mutex> lock(mRequestBufferInterfaceLock);
-
- hardware::hidl_vec<StreamBufferRet> bufRets;
- if (!mUseHalBufManager) {
- ALOGE("%s: Camera %s does not support HAL buffer management",
- __FUNCTION__, mId.string());
- _hidl_cb(BufferRequestStatus::FAILED_ILLEGAL_ARGUMENTS, bufRets);
- return hardware::Void();
- }
-
- SortedVector<int32_t> streamIds;
- ssize_t sz = streamIds.setCapacity(bufReqs.size());
- if (sz < 0 || static_cast<size_t>(sz) != bufReqs.size()) {
- ALOGE("%s: failed to allocate memory for %zu buffer requests",
- __FUNCTION__, bufReqs.size());
- _hidl_cb(BufferRequestStatus::FAILED_ILLEGAL_ARGUMENTS, bufRets);
- return hardware::Void();
- }
-
- if (bufReqs.size() > mOutputStreams.size()) {
- ALOGE("%s: too many buffer requests (%zu > # of output streams %zu)",
- __FUNCTION__, bufReqs.size(), mOutputStreams.size());
- _hidl_cb(BufferRequestStatus::FAILED_ILLEGAL_ARGUMENTS, bufRets);
- return hardware::Void();
- }
-
- // Check for repeated streamId
- for (const auto& bufReq : bufReqs) {
- if (streamIds.indexOf(bufReq.streamId) != NAME_NOT_FOUND) {
- ALOGE("%s: Stream %d appear multiple times in buffer requests",
- __FUNCTION__, bufReq.streamId);
- _hidl_cb(BufferRequestStatus::FAILED_ILLEGAL_ARGUMENTS, bufRets);
- return hardware::Void();
- }
- streamIds.add(bufReq.streamId);
- }
-
- if (!mRequestBufferSM.startRequestBuffer()) {
- ALOGE("%s: request buffer disallowed while camera service is configuring",
- __FUNCTION__);
- _hidl_cb(BufferRequestStatus::FAILED_CONFIGURING, bufRets);
- return hardware::Void();
- }
-
- bufRets.resize(bufReqs.size());
-
- bool allReqsSucceeds = true;
- bool oneReqSucceeds = false;
- for (size_t i = 0; i < bufReqs.size(); i++) {
- const auto& bufReq = bufReqs[i];
- auto& bufRet = bufRets[i];
- int32_t streamId = bufReq.streamId;
- sp<Camera3OutputStreamInterface> outputStream = mOutputStreams.get(streamId);
- if (outputStream == nullptr) {
- ALOGE("%s: Output stream id %d not found!", __FUNCTION__, streamId);
- hardware::hidl_vec<StreamBufferRet> emptyBufRets;
- _hidl_cb(BufferRequestStatus::FAILED_ILLEGAL_ARGUMENTS, emptyBufRets);
- mRequestBufferSM.endRequestBuffer();
- return hardware::Void();
- }
-
- if (outputStream->isAbandoned()) {
- bufRet.val.error(StreamBufferRequestError::STREAM_DISCONNECTED);
- allReqsSucceeds = false;
- continue;
- }
-
- bufRet.streamId = streamId;
- size_t handOutBufferCount = outputStream->getOutstandingBuffersCount();
- uint32_t numBuffersRequested = bufReq.numBuffersRequested;
- size_t totalHandout = handOutBufferCount + numBuffersRequested;
- uint32_t maxBuffers = outputStream->asHalStream()->max_buffers;
- if (totalHandout > maxBuffers) {
- // Not able to allocate enough buffer. Exit early for this stream
- ALOGE("%s: request too much buffers for stream %d: at HAL: %zu + requesting: %d"
- " > max: %d", __FUNCTION__, streamId, handOutBufferCount,
- numBuffersRequested, maxBuffers);
- bufRet.val.error(StreamBufferRequestError::MAX_BUFFER_EXCEEDED);
- allReqsSucceeds = false;
- continue;
- }
-
- hardware::hidl_vec<StreamBuffer> tmpRetBuffers(numBuffersRequested);
- bool currentReqSucceeds = true;
- std::vector<camera3_stream_buffer_t> streamBuffers(numBuffersRequested);
- size_t numAllocatedBuffers = 0;
- size_t numPushedInflightBuffers = 0;
- for (size_t b = 0; b < numBuffersRequested; b++) {
- camera3_stream_buffer_t& sb = streamBuffers[b];
- // Since this method can run concurrently with request thread
- // We need to update the wait duration everytime we call getbuffer
- nsecs_t waitDuration = kBaseGetBufferWait + getExpectedInFlightDuration();
- status_t res = outputStream->getBuffer(&sb, waitDuration);
- if (res != OK) {
- if (res == NO_INIT || res == DEAD_OBJECT) {
- ALOGV("%s: Can't get output buffer for stream %d: %s (%d)",
- __FUNCTION__, streamId, strerror(-res), res);
- bufRet.val.error(StreamBufferRequestError::STREAM_DISCONNECTED);
- } else {
- ALOGE("%s: Can't get output buffer for stream %d: %s (%d)",
- __FUNCTION__, streamId, strerror(-res), res);
- if (res == TIMED_OUT || res == NO_MEMORY) {
- bufRet.val.error(StreamBufferRequestError::NO_BUFFER_AVAILABLE);
- } else {
- bufRet.val.error(StreamBufferRequestError::UNKNOWN_ERROR);
- }
- }
- currentReqSucceeds = false;
- break;
- }
- numAllocatedBuffers++;
-
- buffer_handle_t *buffer = sb.buffer;
- auto pair = mInterface->getBufferId(*buffer, streamId);
- bool isNewBuffer = pair.first;
- uint64_t bufferId = pair.second;
- StreamBuffer& hBuf = tmpRetBuffers[b];
-
- hBuf.streamId = streamId;
- hBuf.bufferId = bufferId;
- hBuf.buffer = (isNewBuffer) ? *buffer : nullptr;
- hBuf.status = BufferStatus::OK;
- hBuf.releaseFence = nullptr;
-
- native_handle_t *acquireFence = nullptr;
- if (sb.acquire_fence != -1) {
- acquireFence = native_handle_create(1,0);
- acquireFence->data[0] = sb.acquire_fence;
- }
- hBuf.acquireFence.setTo(acquireFence, /*shouldOwn*/true);
- hBuf.releaseFence = nullptr;
-
- res = mInterface->pushInflightRequestBuffer(bufferId, buffer, streamId);
- if (res != OK) {
- ALOGE("%s: Can't get register request buffers for stream %d: %s (%d)",
- __FUNCTION__, streamId, strerror(-res), res);
- bufRet.val.error(StreamBufferRequestError::UNKNOWN_ERROR);
- currentReqSucceeds = false;
- break;
- }
- numPushedInflightBuffers++;
- }
- if (currentReqSucceeds) {
- bufRet.val.buffers(std::move(tmpRetBuffers));
- oneReqSucceeds = true;
- } else {
- allReqsSucceeds = false;
- for (size_t b = 0; b < numPushedInflightBuffers; b++) {
- StreamBuffer& hBuf = tmpRetBuffers[b];
- buffer_handle_t* buffer;
- status_t res = mInterface->popInflightRequestBuffer(hBuf.bufferId, &buffer);
- if (res != OK) {
- SET_ERR("%s: popInflightRequestBuffer failed for stream %d: %s (%d)",
- __FUNCTION__, streamId, strerror(-res), res);
- }
- }
- for (size_t b = 0; b < numAllocatedBuffers; b++) {
- camera3_stream_buffer_t& sb = streamBuffers[b];
- sb.acquire_fence = -1;
- sb.status = CAMERA3_BUFFER_STATUS_ERROR;
- }
- returnOutputBuffers(streamBuffers.data(), numAllocatedBuffers, 0);
- }
- }
-
- _hidl_cb(allReqsSucceeds ? BufferRequestStatus::OK :
- oneReqSucceeds ? BufferRequestStatus::FAILED_PARTIAL :
- BufferRequestStatus::FAILED_UNKNOWN,
- bufRets);
- mRequestBufferSM.endRequestBuffer();
+ RequestBufferStates states {
+ mId, mRequestBufferInterfaceLock, mUseHalBufManager, mOutputStreams,
+ *this, *mInterface, *this};
+ camera3::requestStreamBuffers(states, bufReqs, _hidl_cb);
return hardware::Void();
}
hardware::Return<void> Camera3Device::returnStreamBuffers(
const hardware::hidl_vec<hardware::camera::device::V3_2::StreamBuffer>& buffers) {
- if (!mUseHalBufManager) {
- ALOGE("%s: Camera %s does not support HAL buffer managerment",
- __FUNCTION__, mId.string());
- return hardware::Void();
- }
-
- for (const auto& buf : buffers) {
- if (buf.bufferId == HalInterface::BUFFER_ID_NO_BUFFER) {
- ALOGE("%s: cannot return a buffer without bufferId", __FUNCTION__);
- continue;
- }
-
- buffer_handle_t* buffer;
- status_t res = mInterface->popInflightRequestBuffer(buf.bufferId, &buffer);
-
- if (res != OK) {
- ALOGE("%s: cannot find in-flight buffer %" PRIu64 " for stream %d",
- __FUNCTION__, buf.bufferId, buf.streamId);
- continue;
- }
-
- camera3_stream_buffer_t streamBuffer;
- streamBuffer.buffer = buffer;
- streamBuffer.status = CAMERA3_BUFFER_STATUS_ERROR;
- streamBuffer.acquire_fence = -1;
- streamBuffer.release_fence = -1;
-
- if (buf.releaseFence == nullptr) {
- streamBuffer.release_fence = -1;
- } else if (buf.releaseFence->numFds == 1) {
- streamBuffer.release_fence = dup(buf.releaseFence->data[0]);
- } else {
- ALOGE("%s: Invalid release fence, fd count is %d, not 1",
- __FUNCTION__, buf.releaseFence->numFds);
- continue;
- }
-
- sp<Camera3StreamInterface> stream = mOutputStreams.get(buf.streamId);
- if (stream == nullptr) {
- ALOGE("%s: Output stream id %d not found!", __FUNCTION__, buf.streamId);
- continue;
- }
- streamBuffer.stream = stream->asHalStream();
- returnOutputBuffers(&streamBuffer, /*size*/1, /*timestamp*/ 0);
- }
+ ReturnBufferStates states {
+ mId, mUseHalBufManager, mOutputStreams, *mInterface};
+ camera3::returnStreamBuffers(states, buffers);
return hardware::Void();
}
@@ -1220,6 +1020,12 @@
ALOGW("%s: received capture result in error state.", __FUNCTION__);
}
+ sp<NotificationListener> listener;
+ {
+ std::lock_guard<std::mutex> l(mOutputLock);
+ listener = mListener.promote();
+ }
+
if (mProcessCaptureResultLock.tryLock() != OK) {
// This should never happen; it indicates a wrong client implementation
// that doesn't follow the contract. But, we can be tolerant here.
@@ -1232,8 +1038,22 @@
return hardware::Void();
}
}
+ CaptureOutputStates states {
+ mId,
+ mInFlightLock, mInFlightMap,
+ mOutputLock, mResultQueue, mResultSignal,
+ mNextShutterFrameNumber,
+ mNextReprocessShutterFrameNumber, mNextZslStillShutterFrameNumber,
+ mNextResultFrameNumber,
+ mNextReprocessResultFrameNumber, mNextZslStillResultFrameNumber,
+ mUseHalBufManager, mUsePartialResult, mNeedFixupMonochromeTags,
+ mNumPartialResults, mVendorTagId, mDeviceInfo, mPhysicalDeviceInfoMap,
+ mResultMetadataQueue, mDistortionMappers, mZoomRatioMappers, mRotateAndCropMappers,
+ mTagMonitor, mInputStream, mOutputStreams, listener, *this, *this, *mInterface
+ };
+
for (const auto& result : results) {
- processOneCaptureResultLocked(result.v3_2, result.physicalCameraMetadata);
+ processOneCaptureResultLocked(states, result.v3_2, result.physicalCameraMetadata);
}
mProcessCaptureResultLock.unlock();
return hardware::Void();
@@ -1256,6 +1076,12 @@
ALOGW("%s: received capture result in error state.", __FUNCTION__);
}
+ sp<NotificationListener> listener;
+ {
+ std::lock_guard<std::mutex> l(mOutputLock);
+ listener = mListener.promote();
+ }
+
if (mProcessCaptureResultLock.tryLock() != OK) {
// This should never happen; it indicates a wrong client implementation
// that doesn't follow the contract. But, we can be tolerant here.
@@ -1268,186 +1094,28 @@
return hardware::Void();
}
}
+
+ CaptureOutputStates states {
+ mId,
+ mInFlightLock, mInFlightMap,
+ mOutputLock, mResultQueue, mResultSignal,
+ mNextShutterFrameNumber,
+ mNextReprocessShutterFrameNumber, mNextZslStillShutterFrameNumber,
+ mNextResultFrameNumber,
+ mNextReprocessResultFrameNumber, mNextZslStillResultFrameNumber,
+ mUseHalBufManager, mUsePartialResult, mNeedFixupMonochromeTags,
+ mNumPartialResults, mVendorTagId, mDeviceInfo, mPhysicalDeviceInfoMap,
+ mResultMetadataQueue, mDistortionMappers, mZoomRatioMappers, mRotateAndCropMappers,
+ mTagMonitor, mInputStream, mOutputStreams, listener, *this, *this, *mInterface
+ };
+
for (const auto& result : results) {
- processOneCaptureResultLocked(result, noPhysMetadata);
+ processOneCaptureResultLocked(states, result, noPhysMetadata);
}
mProcessCaptureResultLock.unlock();
return hardware::Void();
}
-status_t Camera3Device::readOneCameraMetadataLocked(
- uint64_t fmqResultSize, hardware::camera::device::V3_2::CameraMetadata& resultMetadata,
- const hardware::camera::device::V3_2::CameraMetadata& result) {
- if (fmqResultSize > 0) {
- resultMetadata.resize(fmqResultSize);
- if (mResultMetadataQueue == nullptr) {
- return NO_MEMORY; // logged in initialize()
- }
- if (!mResultMetadataQueue->read(resultMetadata.data(), fmqResultSize)) {
- ALOGE("%s: Cannot read camera metadata from fmq, size = %" PRIu64,
- __FUNCTION__, fmqResultSize);
- return INVALID_OPERATION;
- }
- } else {
- resultMetadata.setToExternal(const_cast<uint8_t *>(result.data()),
- result.size());
- }
-
- if (resultMetadata.size() != 0) {
- status_t res;
- const camera_metadata_t* metadata =
- reinterpret_cast<const camera_metadata_t*>(resultMetadata.data());
- size_t expected_metadata_size = resultMetadata.size();
- if ((res = validate_camera_metadata_structure(metadata, &expected_metadata_size)) != OK) {
- ALOGE("%s: Invalid camera metadata received by camera service from HAL: %s (%d)",
- __FUNCTION__, strerror(-res), res);
- return INVALID_OPERATION;
- }
- }
-
- return OK;
-}
-
-void Camera3Device::processOneCaptureResultLocked(
- const hardware::camera::device::V3_2::CaptureResult& result,
- const hardware::hidl_vec<
- hardware::camera::device::V3_4::PhysicalCameraMetadata> physicalCameraMetadata) {
- camera3_capture_result r;
- status_t res;
- r.frame_number = result.frameNumber;
-
- // Read and validate the result metadata.
- hardware::camera::device::V3_2::CameraMetadata resultMetadata;
- res = readOneCameraMetadataLocked(result.fmqResultSize, resultMetadata, result.result);
- if (res != OK) {
- ALOGE("%s: Frame %d: Failed to read capture result metadata",
- __FUNCTION__, result.frameNumber);
- return;
- }
- r.result = reinterpret_cast<const camera_metadata_t*>(resultMetadata.data());
-
- // Read and validate physical camera metadata
- size_t physResultCount = physicalCameraMetadata.size();
- std::vector<const char*> physCamIds(physResultCount);
- std::vector<const camera_metadata_t *> phyCamMetadatas(physResultCount);
- std::vector<hardware::camera::device::V3_2::CameraMetadata> physResultMetadata;
- physResultMetadata.resize(physResultCount);
- for (size_t i = 0; i < physicalCameraMetadata.size(); i++) {
- res = readOneCameraMetadataLocked(physicalCameraMetadata[i].fmqMetadataSize,
- physResultMetadata[i], physicalCameraMetadata[i].metadata);
- if (res != OK) {
- ALOGE("%s: Frame %d: Failed to read capture result metadata for camera %s",
- __FUNCTION__, result.frameNumber,
- physicalCameraMetadata[i].physicalCameraId.c_str());
- return;
- }
- physCamIds[i] = physicalCameraMetadata[i].physicalCameraId.c_str();
- phyCamMetadatas[i] = reinterpret_cast<const camera_metadata_t*>(
- physResultMetadata[i].data());
- }
- r.num_physcam_metadata = physResultCount;
- r.physcam_ids = physCamIds.data();
- r.physcam_metadata = phyCamMetadatas.data();
-
- std::vector<camera3_stream_buffer_t> outputBuffers(result.outputBuffers.size());
- std::vector<buffer_handle_t> outputBufferHandles(result.outputBuffers.size());
- for (size_t i = 0; i < result.outputBuffers.size(); i++) {
- auto& bDst = outputBuffers[i];
- const StreamBuffer &bSrc = result.outputBuffers[i];
-
- sp<Camera3StreamInterface> stream = mOutputStreams.get(bSrc.streamId);
- if (stream == nullptr) {
- ALOGE("%s: Frame %d: Buffer %zu: Invalid output stream id %d",
- __FUNCTION__, result.frameNumber, i, bSrc.streamId);
- return;
- }
- bDst.stream = stream->asHalStream();
-
- bool noBufferReturned = false;
- buffer_handle_t *buffer = nullptr;
- if (mUseHalBufManager) {
- // This is suspicious most of the time but can be correct during flush where HAL
- // has to return capture result before a buffer is requested
- if (bSrc.bufferId == HalInterface::BUFFER_ID_NO_BUFFER) {
- if (bSrc.status == BufferStatus::OK) {
- ALOGE("%s: Frame %d: Buffer %zu: No bufferId for stream %d",
- __FUNCTION__, result.frameNumber, i, bSrc.streamId);
- // Still proceeds so other buffers can be returned
- }
- noBufferReturned = true;
- }
- if (noBufferReturned) {
- res = OK;
- } else {
- res = mInterface->popInflightRequestBuffer(bSrc.bufferId, &buffer);
- }
- } else {
- res = mInterface->popInflightBuffer(result.frameNumber, bSrc.streamId, &buffer);
- }
-
- if (res != OK) {
- ALOGE("%s: Frame %d: Buffer %zu: No in-flight buffer for stream %d",
- __FUNCTION__, result.frameNumber, i, bSrc.streamId);
- return;
- }
-
- bDst.buffer = buffer;
- bDst.status = mapHidlBufferStatus(bSrc.status);
- bDst.acquire_fence = -1;
- if (bSrc.releaseFence == nullptr) {
- bDst.release_fence = -1;
- } else if (bSrc.releaseFence->numFds == 1) {
- if (noBufferReturned) {
- ALOGE("%s: got releaseFence without output buffer!", __FUNCTION__);
- }
- bDst.release_fence = dup(bSrc.releaseFence->data[0]);
- } else {
- ALOGE("%s: Frame %d: Invalid release fence for buffer %zu, fd count is %d, not 1",
- __FUNCTION__, result.frameNumber, i, bSrc.releaseFence->numFds);
- return;
- }
- }
- r.num_output_buffers = outputBuffers.size();
- r.output_buffers = outputBuffers.data();
-
- camera3_stream_buffer_t inputBuffer;
- if (result.inputBuffer.streamId == -1) {
- r.input_buffer = nullptr;
- } else {
- if (mInputStream->getId() != result.inputBuffer.streamId) {
- ALOGE("%s: Frame %d: Invalid input stream id %d", __FUNCTION__,
- result.frameNumber, result.inputBuffer.streamId);
- return;
- }
- inputBuffer.stream = mInputStream->asHalStream();
- buffer_handle_t *buffer;
- res = mInterface->popInflightBuffer(result.frameNumber, result.inputBuffer.streamId,
- &buffer);
- if (res != OK) {
- ALOGE("%s: Frame %d: Input buffer: No in-flight buffer for stream %d",
- __FUNCTION__, result.frameNumber, result.inputBuffer.streamId);
- return;
- }
- inputBuffer.buffer = buffer;
- inputBuffer.status = mapHidlBufferStatus(result.inputBuffer.status);
- inputBuffer.acquire_fence = -1;
- if (result.inputBuffer.releaseFence == nullptr) {
- inputBuffer.release_fence = -1;
- } else if (result.inputBuffer.releaseFence->numFds == 1) {
- inputBuffer.release_fence = dup(result.inputBuffer.releaseFence->data[0]);
- } else {
- ALOGE("%s: Frame %d: Invalid release fence for input buffer, fd count is %d, not 1",
- __FUNCTION__, result.frameNumber, result.inputBuffer.releaseFence->numFds);
- return;
- }
- r.input_buffer = &inputBuffer;
- }
-
- r.partial_result = result.partialResult;
-
- processCaptureResult(&r);
-}
-
hardware::Return<void> Camera3Device::notify(
const hardware::hidl_vec<hardware::camera::device::V3_2::NotifyMsg>& msgs) {
// Ideally we should grab mLock, but that can lead to deadlock, and
@@ -1460,55 +1128,31 @@
ALOGW("%s: received notify message in error state.", __FUNCTION__);
}
+ sp<NotificationListener> listener;
+ {
+ std::lock_guard<std::mutex> l(mOutputLock);
+ listener = mListener.promote();
+ }
+
+ CaptureOutputStates states {
+ mId,
+ mInFlightLock, mInFlightMap,
+ mOutputLock, mResultQueue, mResultSignal,
+ mNextShutterFrameNumber,
+ mNextReprocessShutterFrameNumber, mNextZslStillShutterFrameNumber,
+ mNextResultFrameNumber,
+ mNextReprocessResultFrameNumber, mNextZslStillResultFrameNumber,
+ mUseHalBufManager, mUsePartialResult, mNeedFixupMonochromeTags,
+ mNumPartialResults, mVendorTagId, mDeviceInfo, mPhysicalDeviceInfoMap,
+ mResultMetadataQueue, mDistortionMappers, mZoomRatioMappers, mRotateAndCropMappers,
+ mTagMonitor, mInputStream, mOutputStreams, listener, *this, *this, *mInterface
+ };
for (const auto& msg : msgs) {
- notify(msg);
+ camera3::notify(states, msg);
}
return hardware::Void();
}
-void Camera3Device::notify(
- const hardware::camera::device::V3_2::NotifyMsg& msg) {
-
- camera3_notify_msg m;
- switch (msg.type) {
- case MsgType::ERROR:
- m.type = CAMERA3_MSG_ERROR;
- m.message.error.frame_number = msg.msg.error.frameNumber;
- if (msg.msg.error.errorStreamId >= 0) {
- sp<Camera3StreamInterface> stream = mOutputStreams.get(msg.msg.error.errorStreamId);
- if (stream == nullptr) {
- ALOGE("%s: Frame %d: Invalid error stream id %d", __FUNCTION__,
- m.message.error.frame_number, msg.msg.error.errorStreamId);
- return;
- }
- m.message.error.error_stream = stream->asHalStream();
- } else {
- m.message.error.error_stream = nullptr;
- }
- switch (msg.msg.error.errorCode) {
- case ErrorCode::ERROR_DEVICE:
- m.message.error.error_code = CAMERA3_MSG_ERROR_DEVICE;
- break;
- case ErrorCode::ERROR_REQUEST:
- m.message.error.error_code = CAMERA3_MSG_ERROR_REQUEST;
- break;
- case ErrorCode::ERROR_RESULT:
- m.message.error.error_code = CAMERA3_MSG_ERROR_RESULT;
- break;
- case ErrorCode::ERROR_BUFFER:
- m.message.error.error_code = CAMERA3_MSG_ERROR_BUFFER;
- break;
- }
- break;
- case MsgType::SHUTTER:
- m.type = CAMERA3_MSG_SHUTTER;
- m.message.shutter.frame_number = msg.msg.shutter.frameNumber;
- m.message.shutter.timestamp = msg.msg.shutter.timestamp;
- break;
- }
- notify(&m);
-}
-
status_t Camera3Device::captureList(const List<const PhysicalCameraSettingsList> &requestsList,
const std::list<const SurfaceMap> &surfaceMaps,
int64_t *lastFrameNumber) {
@@ -1662,56 +1306,6 @@
return OK;
}
-status_t Camera3Device::StreamSet::add(
- int streamId, sp<camera3::Camera3OutputStreamInterface> stream) {
- if (stream == nullptr) {
- ALOGE("%s: cannot add null stream", __FUNCTION__);
- return BAD_VALUE;
- }
- std::lock_guard<std::mutex> lock(mLock);
- return mData.add(streamId, stream);
-}
-
-ssize_t Camera3Device::StreamSet::remove(int streamId) {
- std::lock_guard<std::mutex> lock(mLock);
- return mData.removeItem(streamId);
-}
-
-sp<camera3::Camera3OutputStreamInterface>
-Camera3Device::StreamSet::get(int streamId) {
- std::lock_guard<std::mutex> lock(mLock);
- ssize_t idx = mData.indexOfKey(streamId);
- if (idx == NAME_NOT_FOUND) {
- return nullptr;
- }
- return mData.editValueAt(idx);
-}
-
-sp<camera3::Camera3OutputStreamInterface>
-Camera3Device::StreamSet::operator[] (size_t index) {
- std::lock_guard<std::mutex> lock(mLock);
- return mData.editValueAt(index);
-}
-
-size_t Camera3Device::StreamSet::size() const {
- std::lock_guard<std::mutex> lock(mLock);
- return mData.size();
-}
-
-void Camera3Device::StreamSet::clear() {
- std::lock_guard<std::mutex> lock(mLock);
- return mData.clear();
-}
-
-std::vector<int> Camera3Device::StreamSet::getStreamIds() {
- std::lock_guard<std::mutex> lock(mLock);
- std::vector<int> streamIds(mData.size());
- for (size_t i = 0; i < mData.size(); i++) {
- streamIds[i] = mData.keyAt(i);
- }
- return streamIds;
-}
-
status_t Camera3Device::createStream(sp<Surface> consumer,
uint32_t width, uint32_t height, int format,
android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
@@ -2116,6 +1710,15 @@
set_camera_metadata_vendor_id(rawRequest, mVendorTagId);
mRequestTemplateCache[templateId].acquire(rawRequest);
+ // Override the template request with zoomRatioMapper
+ res = mZoomRatioMappers[mId.c_str()].initZoomRatioInTemplate(
+ &mRequestTemplateCache[templateId]);
+ if (res != OK) {
+ CLOGE("Failed to update zoom ratio for template %d: %s (%d)",
+ templateId, strerror(-res), res);
+ return res;
+ }
+
*request = mRequestTemplateCache[templateId];
mLastTemplateId = templateId;
}
@@ -2165,7 +1768,9 @@
}
void Camera3Device::pauseStateNotify(bool enable) {
- Mutex::Autolock il(mInterfaceLock);
+ // We must not hold mInterfaceLock here since this function is called from
+ // RequestThread::threadLoop and holding mInterfaceLock could lead to
+ // deadlocks (http://b/143513518)
Mutex::Autolock l(mLock);
mPauseStateNotify = enable;
@@ -2264,7 +1869,7 @@
status_t Camera3Device::setNotifyCallback(wp<NotificationListener> listener) {
ATRACE_CALL();
- Mutex::Autolock l(mOutputLock);
+ std::lock_guard<std::mutex> l(mOutputLock);
if (listener != NULL && mListener != NULL) {
ALOGW("%s: Replacing old callback listener", __FUNCTION__);
@@ -2282,17 +1887,12 @@
status_t Camera3Device::waitForNextFrame(nsecs_t timeout) {
ATRACE_CALL();
- status_t res;
- Mutex::Autolock l(mOutputLock);
+ std::unique_lock<std::mutex> l(mOutputLock);
while (mResultQueue.empty()) {
- res = mResultSignal.waitRelative(mOutputLock, timeout);
- if (res == TIMED_OUT) {
- return res;
- } else if (res != OK) {
- ALOGW("%s: Camera %s: No frame in %" PRId64 " ns: %s (%d)",
- __FUNCTION__, mId.string(), timeout, strerror(-res), res);
- return res;
+ auto st = mResultSignal.wait_for(l, std::chrono::nanoseconds(timeout));
+ if (st == std::cv_status::timeout) {
+ return TIMED_OUT;
}
}
return OK;
@@ -2300,7 +1900,7 @@
status_t Camera3Device::getNextResult(CaptureResult *frame) {
ATRACE_CALL();
- Mutex::Autolock l(mOutputLock);
+ std::lock_guard<std::mutex> l(mOutputLock);
if (mResultQueue.empty()) {
return NOT_ENOUGH_DATA;
@@ -2496,7 +2096,7 @@
sp<NotificationListener> listener;
{
- Mutex::Autolock l(mOutputLock);
+ std::lock_guard<std::mutex> l(mOutputLock);
listener = mListener.promote();
}
if (idle && listener != NULL) {
@@ -2621,7 +2221,7 @@
const PhysicalCameraSettingsList &request, const SurfaceMap &surfaceMap) {
ATRACE_CALL();
- sp<CaptureRequest> newRequest = new CaptureRequest;
+ sp<CaptureRequest> newRequest = new CaptureRequest();
newRequest->mSettingsList = request;
camera_metadata_entry_t inputStreams =
@@ -2692,18 +2292,16 @@
newRequest->mSettingsList.begin()->metadata.erase(ANDROID_REQUEST_OUTPUT_STREAMS);
newRequest->mBatchSize = 1;
- return newRequest;
-}
-
-bool Camera3Device::isOpaqueInputSizeSupported(uint32_t width, uint32_t height) {
- for (uint32_t i = 0; i < mSupportedOpaqueInputSizes.size(); i++) {
- Size size = mSupportedOpaqueInputSizes[i];
- if (size.width == width && size.height == height) {
- return true;
- }
+ auto rotateAndCropEntry =
+ newRequest->mSettingsList.begin()->metadata.find(ANDROID_SCALER_ROTATE_AND_CROP);
+ if (rotateAndCropEntry.count > 0 &&
+ rotateAndCropEntry.data.u8[0] == ANDROID_SCALER_ROTATE_AND_CROP_AUTO) {
+ newRequest->mRotateAndCropAuto = true;
+ } else {
+ newRequest->mRotateAndCropAuto = false;
}
- return false;
+ return newRequest;
}
void Camera3Device::cancelStreamsConfigurationLocked() {
@@ -2742,7 +2340,9 @@
ATRACE_CALL();
bool ret = false;
- Mutex::Autolock il(mInterfaceLock);
+ // We must not hold mInterfaceLock here since this function is called from
+ // RequestThread::threadLoop and holding mInterfaceLock could lead to
+ // deadlocks (http://b/143513518)
nsecs_t maxExpectedDuration = getExpectedInFlightDuration();
Mutex::Autolock l(mLock);
@@ -3138,14 +2738,15 @@
int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput,
bool hasAppCallback, nsecs_t maxExpectedDuration,
std::set<String8>& physicalCameraIds, bool isStillCapture,
- bool isZslCapture, const SurfaceMap& outputSurfaces) {
+ bool isZslCapture, bool rotateAndCropAuto, const std::set<std::string>& cameraIdsWithZoom,
+ const SurfaceMap& outputSurfaces) {
ATRACE_CALL();
- Mutex::Autolock l(mInFlightLock);
+ std::lock_guard<std::mutex> l(mInFlightLock);
ssize_t res;
res = mInFlightMap.add(frameNumber, InFlightRequest(numBuffers, resultExtras, hasInput,
hasAppCallback, maxExpectedDuration, physicalCameraIds, isStillCapture, isZslCapture,
- outputSurfaces));
+ rotateAndCropAuto, cameraIdsWithZoom, outputSurfaces));
if (res < 0) return res;
if (mInFlightMap.size() == 1) {
@@ -3161,79 +2762,7 @@
return OK;
}
-void Camera3Device::returnOutputBuffers(
- const camera3_stream_buffer_t *outputBuffers, size_t numBuffers,
- nsecs_t timestamp, bool timestampIncreasing,
- const SurfaceMap& outputSurfaces,
- const CaptureResultExtras &inResultExtras) {
-
- for (size_t i = 0; i < numBuffers; i++)
- {
- if (outputBuffers[i].buffer == nullptr) {
- if (!mUseHalBufManager) {
- // With HAL buffer management API, HAL sometimes will have to return buffers that
- // has not got a output buffer handle filled yet. This is though illegal if HAL
- // buffer management API is not being used.
- ALOGE("%s: cannot return a null buffer!", __FUNCTION__);
- }
- continue;
- }
-
- Camera3StreamInterface *stream = Camera3Stream::cast(outputBuffers[i].stream);
- int streamId = stream->getId();
- const auto& it = outputSurfaces.find(streamId);
- status_t res = OK;
- if (it != outputSurfaces.end()) {
- res = stream->returnBuffer(
- outputBuffers[i], timestamp, timestampIncreasing, it->second,
- inResultExtras.frameNumber);
- } else {
- res = stream->returnBuffer(
- outputBuffers[i], timestamp, timestampIncreasing, std::vector<size_t> (),
- inResultExtras.frameNumber);
- }
-
- // Note: stream may be deallocated at this point, if this buffer was
- // the last reference to it.
- if (res == NO_INIT || res == DEAD_OBJECT) {
- ALOGV("Can't return buffer to its stream: %s (%d)", strerror(-res), res);
- } else if (res != OK) {
- ALOGE("Can't return buffer to its stream: %s (%d)", strerror(-res), res);
- }
-
- // Long processing consumers can cause returnBuffer timeout for shared stream
- // If that happens, cancel the buffer and send a buffer error to client
- if (it != outputSurfaces.end() && res == TIMED_OUT &&
- outputBuffers[i].status == CAMERA3_BUFFER_STATUS_OK) {
- // cancel the buffer
- camera3_stream_buffer_t sb = outputBuffers[i];
- sb.status = CAMERA3_BUFFER_STATUS_ERROR;
- stream->returnBuffer(sb, /*timestamp*/0, timestampIncreasing, std::vector<size_t> (),
- inResultExtras.frameNumber);
-
- // notify client buffer error
- sp<NotificationListener> listener;
- {
- Mutex::Autolock l(mOutputLock);
- listener = mListener.promote();
- }
-
- if (listener != nullptr) {
- CaptureResultExtras extras = inResultExtras;
- extras.errorStreamId = streamId;
- listener->notifyError(
- hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER,
- extras);
- }
- }
- }
-}
-
-void Camera3Device::removeInFlightMapEntryLocked(int idx) {
- ATRACE_CALL();
- nsecs_t duration = mInFlightMap.valueAt(idx).maxExpectedDuration;
- mInFlightMap.removeItemsAt(idx, 1);
-
+void Camera3Device::onInflightEntryRemovedLocked(nsecs_t duration) {
// Indicate idle inFlightMap to the status tracker
if (mInFlightMap.size() == 0) {
mRequestBufferSM.onInflightMapEmpty();
@@ -3247,50 +2776,7 @@
mExpectedInflightDuration -= duration;
}
-void Camera3Device::removeInFlightRequestIfReadyLocked(int idx) {
-
- const InFlightRequest &request = mInFlightMap.valueAt(idx);
- const uint32_t frameNumber = mInFlightMap.keyAt(idx);
-
- nsecs_t sensorTimestamp = request.sensorTimestamp;
- nsecs_t shutterTimestamp = request.shutterTimestamp;
-
- // Check if it's okay to remove the request from InFlightMap:
- // In the case of a successful request:
- // all input and output buffers, all result metadata, shutter callback
- // arrived.
- // In the case of a unsuccessful request:
- // all input and output buffers arrived.
- if (request.numBuffersLeft == 0 &&
- (request.skipResultMetadata ||
- (request.haveResultMetadata && shutterTimestamp != 0))) {
- if (request.stillCapture) {
- ATRACE_ASYNC_END("still capture", frameNumber);
- }
-
- ATRACE_ASYNC_END("frame capture", frameNumber);
-
- // Sanity check - if sensor timestamp matches shutter timestamp in the
- // case of request having callback.
- if (request.hasCallback && request.requestStatus == OK &&
- sensorTimestamp != shutterTimestamp) {
- SET_ERR("sensor timestamp (%" PRId64
- ") for frame %d doesn't match shutter timestamp (%" PRId64 ")",
- sensorTimestamp, frameNumber, shutterTimestamp);
- }
-
- // for an unsuccessful request, it may have pending output buffers to
- // return.
- assert(request.requestStatus != OK ||
- request.pendingOutputBuffers.size() == 0);
- returnOutputBuffers(request.pendingOutputBuffers.array(),
- request.pendingOutputBuffers.size(), 0, /*timestampIncreasing*/true,
- request.outputSurfaces, request.resultExtras);
-
- removeInFlightMapEntryLocked(idx);
- ALOGVV("%s: removed frame %d from InFlightMap", __FUNCTION__, frameNumber);
- }
-
+void Camera3Device::checkInflightMapLengthLocked() {
// Sanity check - if we have too many in-flight frames with long total inflight duration,
// something has likely gone wrong. This might still be legit only if application send in
// a long burst of long exposure requests.
@@ -3307,714 +2793,32 @@
}
}
+void Camera3Device::onInflightMapFlushedLocked() {
+ mExpectedInflightDuration = 0;
+}
+
+void Camera3Device::removeInFlightMapEntryLocked(int idx) {
+ ATRACE_CALL();
+ nsecs_t duration = mInFlightMap.valueAt(idx).maxExpectedDuration;
+ mInFlightMap.removeItemsAt(idx, 1);
+
+ onInflightEntryRemovedLocked(duration);
+}
+
+
void Camera3Device::flushInflightRequests() {
ATRACE_CALL();
- { // First return buffers cached in mInFlightMap
- Mutex::Autolock l(mInFlightLock);
- for (size_t idx = 0; idx < mInFlightMap.size(); idx++) {
- const InFlightRequest &request = mInFlightMap.valueAt(idx);
- returnOutputBuffers(request.pendingOutputBuffers.array(),
- request.pendingOutputBuffers.size(), 0,
- /*timestampIncreasing*/true, request.outputSurfaces,
- request.resultExtras);
- }
- mInFlightMap.clear();
- mExpectedInflightDuration = 0;
- }
-
- // Then return all inflight buffers not returned by HAL
- std::vector<std::pair<int32_t, int32_t>> inflightKeys;
- mInterface->getInflightBufferKeys(&inflightKeys);
-
- // Inflight buffers for HAL buffer manager
- std::vector<uint64_t> inflightRequestBufferKeys;
- mInterface->getInflightRequestBufferKeys(&inflightRequestBufferKeys);
-
- // (streamId, frameNumber, buffer_handle_t*) tuple for all inflight buffers.
- // frameNumber will be -1 for buffers from HAL buffer manager
- std::vector<std::tuple<int32_t, int32_t, buffer_handle_t*>> inflightBuffers;
- inflightBuffers.reserve(inflightKeys.size() + inflightRequestBufferKeys.size());
-
- for (auto& pair : inflightKeys) {
- int32_t frameNumber = pair.first;
- int32_t streamId = pair.second;
- buffer_handle_t* buffer;
- status_t res = mInterface->popInflightBuffer(frameNumber, streamId, &buffer);
- if (res != OK) {
- ALOGE("%s: Frame %d: No in-flight buffer for stream %d",
- __FUNCTION__, frameNumber, streamId);
- continue;
- }
- inflightBuffers.push_back(std::make_tuple(streamId, frameNumber, buffer));
- }
-
- for (auto& bufferId : inflightRequestBufferKeys) {
- int32_t streamId = -1;
- buffer_handle_t* buffer = nullptr;
- status_t res = mInterface->popInflightRequestBuffer(bufferId, &buffer, &streamId);
- if (res != OK) {
- ALOGE("%s: cannot find in-flight buffer %" PRIu64, __FUNCTION__, bufferId);
- continue;
- }
- inflightBuffers.push_back(std::make_tuple(streamId, /*frameNumber*/-1, buffer));
- }
-
- int32_t inputStreamId = (mInputStream != nullptr) ? mInputStream->getId() : -1;
- for (auto& tuple : inflightBuffers) {
- status_t res = OK;
- int32_t streamId = std::get<0>(tuple);
- int32_t frameNumber = std::get<1>(tuple);
- buffer_handle_t* buffer = std::get<2>(tuple);
-
- camera3_stream_buffer_t streamBuffer;
- streamBuffer.buffer = buffer;
- streamBuffer.status = CAMERA3_BUFFER_STATUS_ERROR;
- streamBuffer.acquire_fence = -1;
- streamBuffer.release_fence = -1;
-
- // First check if the buffer belongs to deleted stream
- bool streamDeleted = false;
- for (auto& stream : mDeletedStreams) {
- if (streamId == stream->getId()) {
- streamDeleted = true;
- // Return buffer to deleted stream
- camera3_stream* halStream = stream->asHalStream();
- streamBuffer.stream = halStream;
- switch (halStream->stream_type) {
- case CAMERA3_STREAM_OUTPUT:
- res = stream->returnBuffer(streamBuffer, /*timestamp*/ 0,
- /*timestampIncreasing*/true, std::vector<size_t> (), frameNumber);
- if (res != OK) {
- ALOGE("%s: Can't return output buffer for frame %d to"
- " stream %d: %s (%d)", __FUNCTION__,
- frameNumber, streamId, strerror(-res), res);
- }
- break;
- case CAMERA3_STREAM_INPUT:
- res = stream->returnInputBuffer(streamBuffer);
- if (res != OK) {
- ALOGE("%s: Can't return input buffer for frame %d to"
- " stream %d: %s (%d)", __FUNCTION__,
- frameNumber, streamId, strerror(-res), res);
- }
- break;
- default: // Bi-direcitonal stream is deprecated
- ALOGE("%s: stream %d has unknown stream type %d",
- __FUNCTION__, streamId, halStream->stream_type);
- break;
- }
- break;
- }
- }
- if (streamDeleted) {
- continue;
- }
-
- // Then check against configured streams
- if (streamId == inputStreamId) {
- streamBuffer.stream = mInputStream->asHalStream();
- res = mInputStream->returnInputBuffer(streamBuffer);
- if (res != OK) {
- ALOGE("%s: Can't return input buffer for frame %d to"
- " stream %d: %s (%d)", __FUNCTION__,
- frameNumber, streamId, strerror(-res), res);
- }
- } else {
- sp<Camera3StreamInterface> stream = mOutputStreams.get(streamId);
- if (stream == nullptr) {
- ALOGE("%s: Output stream id %d not found!", __FUNCTION__, streamId);
- continue;
- }
- streamBuffer.stream = stream->asHalStream();
- returnOutputBuffers(&streamBuffer, /*size*/1, /*timestamp*/ 0);
- }
- }
-}
-
-void Camera3Device::insertResultLocked(CaptureResult *result,
- uint32_t frameNumber) {
- if (result == nullptr) return;
-
- camera_metadata_t *meta = const_cast<camera_metadata_t *>(
- result->mMetadata.getAndLock());
- set_camera_metadata_vendor_id(meta, mVendorTagId);
- result->mMetadata.unlock(meta);
-
- if (result->mMetadata.update(ANDROID_REQUEST_FRAME_COUNT,
- (int32_t*)&frameNumber, 1) != OK) {
- SET_ERR("Failed to set frame number %d in metadata", frameNumber);
- return;
- }
-
- if (result->mMetadata.update(ANDROID_REQUEST_ID, &result->mResultExtras.requestId, 1) != OK) {
- SET_ERR("Failed to set request ID in metadata for frame %d", frameNumber);
- return;
- }
-
- // Update vendor tag id for physical metadata
- for (auto& physicalMetadata : result->mPhysicalMetadatas) {
- camera_metadata_t *pmeta = const_cast<camera_metadata_t *>(
- physicalMetadata.mPhysicalCameraMetadata.getAndLock());
- set_camera_metadata_vendor_id(pmeta, mVendorTagId);
- physicalMetadata.mPhysicalCameraMetadata.unlock(pmeta);
- }
-
- // Valid result, insert into queue
- List<CaptureResult>::iterator queuedResult =
- mResultQueue.insert(mResultQueue.end(), CaptureResult(*result));
- ALOGVV("%s: result requestId = %" PRId32 ", frameNumber = %" PRId64
- ", burstId = %" PRId32, __FUNCTION__,
- queuedResult->mResultExtras.requestId,
- queuedResult->mResultExtras.frameNumber,
- queuedResult->mResultExtras.burstId);
-
- mResultSignal.signal();
-}
-
-
-void Camera3Device::sendPartialCaptureResult(const camera_metadata_t * partialResult,
- const CaptureResultExtras &resultExtras, uint32_t frameNumber) {
- ATRACE_CALL();
- Mutex::Autolock l(mOutputLock);
-
- CaptureResult captureResult;
- captureResult.mResultExtras = resultExtras;
- captureResult.mMetadata = partialResult;
-
- // Fix up result metadata for monochrome camera.
- status_t res = fixupMonochromeTags(mDeviceInfo, captureResult.mMetadata);
- if (res != OK) {
- SET_ERR("Failed to override result metadata: %s (%d)", strerror(-res), res);
- return;
- }
-
- insertResultLocked(&captureResult, frameNumber);
-}
-
-
-void Camera3Device::sendCaptureResult(CameraMetadata &pendingMetadata,
- CaptureResultExtras &resultExtras,
- CameraMetadata &collectedPartialResult,
- uint32_t frameNumber,
- bool reprocess, bool zslStillCapture,
- const std::vector<PhysicalCaptureResultInfo>& physicalMetadatas) {
- ATRACE_CALL();
- if (pendingMetadata.isEmpty())
- return;
-
- Mutex::Autolock l(mOutputLock);
-
- // TODO: need to track errors for tighter bounds on expected frame number
- if (reprocess) {
- if (frameNumber < mNextReprocessResultFrameNumber) {
- SET_ERR("Out-of-order reprocess capture result metadata submitted! "
- "(got frame number %d, expecting %d)",
- frameNumber, mNextReprocessResultFrameNumber);
- return;
- }
- mNextReprocessResultFrameNumber = frameNumber + 1;
- } else if (zslStillCapture) {
- if (frameNumber < mNextZslStillResultFrameNumber) {
- SET_ERR("Out-of-order ZSL still capture result metadata submitted! "
- "(got frame number %d, expecting %d)",
- frameNumber, mNextZslStillResultFrameNumber);
- return;
- }
- mNextZslStillResultFrameNumber = frameNumber + 1;
- } else {
- if (frameNumber < mNextResultFrameNumber) {
- SET_ERR("Out-of-order capture result metadata submitted! "
- "(got frame number %d, expecting %d)",
- frameNumber, mNextResultFrameNumber);
- return;
- }
- mNextResultFrameNumber = frameNumber + 1;
- }
-
- CaptureResult captureResult;
- captureResult.mResultExtras = resultExtras;
- captureResult.mMetadata = pendingMetadata;
- captureResult.mPhysicalMetadatas = physicalMetadatas;
-
- // Append any previous partials to form a complete result
- if (mUsePartialResult && !collectedPartialResult.isEmpty()) {
- captureResult.mMetadata.append(collectedPartialResult);
- }
-
- captureResult.mMetadata.sort();
-
- // Check that there's a timestamp in the result metadata
- camera_metadata_entry timestamp = captureResult.mMetadata.find(ANDROID_SENSOR_TIMESTAMP);
- if (timestamp.count == 0) {
- SET_ERR("No timestamp provided by HAL for frame %d!",
- frameNumber);
- return;
- }
- for (auto& physicalMetadata : captureResult.mPhysicalMetadatas) {
- camera_metadata_entry timestamp =
- physicalMetadata.mPhysicalCameraMetadata.find(ANDROID_SENSOR_TIMESTAMP);
- if (timestamp.count == 0) {
- SET_ERR("No timestamp provided by HAL for physical camera %s frame %d!",
- String8(physicalMetadata.mPhysicalCameraId).c_str(), frameNumber);
- return;
- }
- }
-
- // Fix up some result metadata to account for HAL-level distortion correction
- status_t res =
- mDistortionMappers[mId.c_str()].correctCaptureResult(&captureResult.mMetadata);
- if (res != OK) {
- SET_ERR("Unable to correct capture result metadata for frame %d: %s (%d)",
- frameNumber, strerror(res), res);
- return;
- }
- for (auto& physicalMetadata : captureResult.mPhysicalMetadatas) {
- String8 cameraId8(physicalMetadata.mPhysicalCameraId);
- if (mDistortionMappers.find(cameraId8.c_str()) == mDistortionMappers.end()) {
- continue;
- }
- res = mDistortionMappers[cameraId8.c_str()].correctCaptureResult(
- &physicalMetadata.mPhysicalCameraMetadata);
- if (res != OK) {
- SET_ERR("Unable to correct physical capture result metadata for frame %d: %s (%d)",
- frameNumber, strerror(res), res);
- return;
- }
- }
-
- // Fix up result metadata for monochrome camera.
- res = fixupMonochromeTags(mDeviceInfo, captureResult.mMetadata);
- if (res != OK) {
- SET_ERR("Failed to override result metadata: %s (%d)", strerror(-res), res);
- return;
- }
- for (auto& physicalMetadata : captureResult.mPhysicalMetadatas) {
- String8 cameraId8(physicalMetadata.mPhysicalCameraId);
- res = fixupMonochromeTags(mPhysicalDeviceInfoMap.at(cameraId8.c_str()),
- physicalMetadata.mPhysicalCameraMetadata);
- if (res != OK) {
- SET_ERR("Failed to override result metadata: %s (%d)", strerror(-res), res);
- return;
- }
- }
-
- std::unordered_map<std::string, CameraMetadata> monitoredPhysicalMetadata;
- for (auto& m : physicalMetadatas) {
- monitoredPhysicalMetadata.emplace(String8(m.mPhysicalCameraId).string(),
- CameraMetadata(m.mPhysicalCameraMetadata));
- }
- mTagMonitor.monitorMetadata(TagMonitor::RESULT,
- frameNumber, timestamp.data.i64[0], captureResult.mMetadata,
- monitoredPhysicalMetadata);
-
- insertResultLocked(&captureResult, frameNumber);
-}
-
-/**
- * Camera HAL device callback methods
- */
-
-void Camera3Device::processCaptureResult(const camera3_capture_result *result) {
- ATRACE_CALL();
-
- status_t res;
-
- uint32_t frameNumber = result->frame_number;
- if (result->result == NULL && result->num_output_buffers == 0 &&
- result->input_buffer == NULL) {
- SET_ERR("No result data provided by HAL for frame %d",
- frameNumber);
- return;
- }
-
- if (!mUsePartialResult &&
- result->result != NULL &&
- result->partial_result != 1) {
- SET_ERR("Result is malformed for frame %d: partial_result %u must be 1"
- " if partial result is not supported",
- frameNumber, result->partial_result);
- return;
- }
-
- bool isPartialResult = false;
- CameraMetadata collectedPartialResult;
- bool hasInputBufferInRequest = false;
-
- // Get shutter timestamp and resultExtras from list of in-flight requests,
- // where it was added by the shutter notification for this frame. If the
- // shutter timestamp isn't received yet, append the output buffers to the
- // in-flight request and they will be returned when the shutter timestamp
- // arrives. Update the in-flight status and remove the in-flight entry if
- // all result data and shutter timestamp have been received.
- nsecs_t shutterTimestamp = 0;
-
- {
- Mutex::Autolock l(mInFlightLock);
- ssize_t idx = mInFlightMap.indexOfKey(frameNumber);
- if (idx == NAME_NOT_FOUND) {
- SET_ERR("Unknown frame number for capture result: %d",
- frameNumber);
- return;
- }
- InFlightRequest &request = mInFlightMap.editValueAt(idx);
- ALOGVV("%s: got InFlightRequest requestId = %" PRId32
- ", frameNumber = %" PRId64 ", burstId = %" PRId32
- ", partialResultCount = %d, hasCallback = %d",
- __FUNCTION__, request.resultExtras.requestId,
- request.resultExtras.frameNumber, request.resultExtras.burstId,
- result->partial_result, request.hasCallback);
- // Always update the partial count to the latest one if it's not 0
- // (buffers only). When framework aggregates adjacent partial results
- // into one, the latest partial count will be used.
- if (result->partial_result != 0)
- request.resultExtras.partialResultCount = result->partial_result;
-
- // Check if this result carries only partial metadata
- if (mUsePartialResult && result->result != NULL) {
- if (result->partial_result > mNumPartialResults || result->partial_result < 1) {
- SET_ERR("Result is malformed for frame %d: partial_result %u must be in"
- " the range of [1, %d] when metadata is included in the result",
- frameNumber, result->partial_result, mNumPartialResults);
- return;
- }
- isPartialResult = (result->partial_result < mNumPartialResults);
- if (isPartialResult && result->num_physcam_metadata) {
- SET_ERR("Result is malformed for frame %d: partial_result not allowed for"
- " physical camera result", frameNumber);
- return;
- }
- if (isPartialResult) {
- request.collectedPartialResult.append(result->result);
- }
-
- if (isPartialResult && request.hasCallback) {
- // Send partial capture result
- sendPartialCaptureResult(result->result, request.resultExtras,
- frameNumber);
- }
- }
-
- shutterTimestamp = request.shutterTimestamp;
- hasInputBufferInRequest = request.hasInputBuffer;
-
- // Did we get the (final) result metadata for this capture?
- if (result->result != NULL && !isPartialResult) {
- if (request.physicalCameraIds.size() != result->num_physcam_metadata) {
- SET_ERR("Expected physical Camera metadata count %d not equal to actual count %d",
- request.physicalCameraIds.size(), result->num_physcam_metadata);
- return;
- }
- if (request.haveResultMetadata) {
- SET_ERR("Called multiple times with metadata for frame %d",
- frameNumber);
- return;
- }
- for (uint32_t i = 0; i < result->num_physcam_metadata; i++) {
- String8 physicalId(result->physcam_ids[i]);
- std::set<String8>::iterator cameraIdIter =
- request.physicalCameraIds.find(physicalId);
- if (cameraIdIter != request.physicalCameraIds.end()) {
- request.physicalCameraIds.erase(cameraIdIter);
- } else {
- SET_ERR("Total result for frame %d has already returned for camera %s",
- frameNumber, physicalId.c_str());
- return;
- }
- }
- if (mUsePartialResult &&
- !request.collectedPartialResult.isEmpty()) {
- collectedPartialResult.acquire(
- request.collectedPartialResult);
- }
- request.haveResultMetadata = true;
- }
-
- uint32_t numBuffersReturned = result->num_output_buffers;
- if (result->input_buffer != NULL) {
- if (hasInputBufferInRequest) {
- numBuffersReturned += 1;
- } else {
- ALOGW("%s: Input buffer should be NULL if there is no input"
- " buffer sent in the request",
- __FUNCTION__);
- }
- }
- request.numBuffersLeft -= numBuffersReturned;
- if (request.numBuffersLeft < 0) {
- SET_ERR("Too many buffers returned for frame %d",
- frameNumber);
- return;
- }
-
- camera_metadata_ro_entry_t entry;
- res = find_camera_metadata_ro_entry(result->result,
- ANDROID_SENSOR_TIMESTAMP, &entry);
- if (res == OK && entry.count == 1) {
- request.sensorTimestamp = entry.data.i64[0];
- }
-
- // If shutter event isn't received yet, append the output buffers to
- // the in-flight request. Otherwise, return the output buffers to
- // streams.
- if (shutterTimestamp == 0) {
- request.pendingOutputBuffers.appendArray(result->output_buffers,
- result->num_output_buffers);
- } else {
- bool timestampIncreasing = !(request.zslCapture || request.hasInputBuffer);
- returnOutputBuffers(result->output_buffers,
- result->num_output_buffers, shutterTimestamp, timestampIncreasing,
- request.outputSurfaces, request.resultExtras);
- }
-
- if (result->result != NULL && !isPartialResult) {
- for (uint32_t i = 0; i < result->num_physcam_metadata; i++) {
- CameraMetadata physicalMetadata;
- physicalMetadata.append(result->physcam_metadata[i]);
- request.physicalMetadatas.push_back({String16(result->physcam_ids[i]),
- physicalMetadata});
- }
- if (shutterTimestamp == 0) {
- request.pendingMetadata = result->result;
- request.collectedPartialResult = collectedPartialResult;
- } else if (request.hasCallback) {
- CameraMetadata metadata;
- metadata = result->result;
- sendCaptureResult(metadata, request.resultExtras,
- collectedPartialResult, frameNumber,
- hasInputBufferInRequest, request.zslCapture && request.stillCapture,
- request.physicalMetadatas);
- }
- }
-
- removeInFlightRequestIfReadyLocked(idx);
- } // scope for mInFlightLock
-
- if (result->input_buffer != NULL) {
- if (hasInputBufferInRequest) {
- Camera3Stream *stream =
- Camera3Stream::cast(result->input_buffer->stream);
- res = stream->returnInputBuffer(*(result->input_buffer));
- // Note: stream may be deallocated at this point, if this buffer was the
- // last reference to it.
- if (res != OK) {
- ALOGE("%s: RequestThread: Can't return input buffer for frame %d to"
- " its stream:%s (%d)", __FUNCTION__,
- frameNumber, strerror(-res), res);
- }
- } else {
- ALOGW("%s: Input buffer should be NULL if there is no input"
- " buffer sent in the request, skipping input buffer return.",
- __FUNCTION__);
- }
- }
-}
-
-void Camera3Device::notify(const camera3_notify_msg *msg) {
- ATRACE_CALL();
sp<NotificationListener> listener;
{
- Mutex::Autolock l(mOutputLock);
+ std::lock_guard<std::mutex> l(mOutputLock);
listener = mListener.promote();
}
- if (msg == NULL) {
- SET_ERR("HAL sent NULL notify message!");
- return;
- }
+ FlushInflightReqStates states {
+ mId, mInFlightLock, mInFlightMap, mUseHalBufManager,
+ listener, *this, *mInterface, *this};
- switch (msg->type) {
- case CAMERA3_MSG_ERROR: {
- notifyError(msg->message.error, listener);
- break;
- }
- case CAMERA3_MSG_SHUTTER: {
- notifyShutter(msg->message.shutter, listener);
- break;
- }
- default:
- SET_ERR("Unknown notify message from HAL: %d",
- msg->type);
- }
-}
-
-void Camera3Device::notifyError(const camera3_error_msg_t &msg,
- sp<NotificationListener> listener) {
- ATRACE_CALL();
- // Map camera HAL error codes to ICameraDeviceCallback error codes
- // Index into this with the HAL error code
- static const int32_t halErrorMap[CAMERA3_MSG_NUM_ERRORS] = {
- // 0 = Unused error code
- hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_INVALID_ERROR,
- // 1 = CAMERA3_MSG_ERROR_DEVICE
- hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DEVICE,
- // 2 = CAMERA3_MSG_ERROR_REQUEST
- hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST,
- // 3 = CAMERA3_MSG_ERROR_RESULT
- hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_RESULT,
- // 4 = CAMERA3_MSG_ERROR_BUFFER
- hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER
- };
-
- int32_t errorCode =
- ((msg.error_code >= 0) &&
- (msg.error_code < CAMERA3_MSG_NUM_ERRORS)) ?
- halErrorMap[msg.error_code] :
- hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_INVALID_ERROR;
-
- int streamId = 0;
- String16 physicalCameraId;
- if (msg.error_stream != NULL) {
- Camera3Stream *stream =
- Camera3Stream::cast(msg.error_stream);
- streamId = stream->getId();
- physicalCameraId = String16(stream->physicalCameraId());
- }
- ALOGV("Camera %s: %s: HAL error, frame %d, stream %d: %d",
- mId.string(), __FUNCTION__, msg.frame_number,
- streamId, msg.error_code);
-
- CaptureResultExtras resultExtras;
- switch (errorCode) {
- case hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DEVICE:
- // SET_ERR calls notifyError
- SET_ERR("Camera HAL reported serious device error");
- break;
- case hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST:
- case hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_RESULT:
- case hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER:
- {
- Mutex::Autolock l(mInFlightLock);
- ssize_t idx = mInFlightMap.indexOfKey(msg.frame_number);
- if (idx >= 0) {
- InFlightRequest &r = mInFlightMap.editValueAt(idx);
- r.requestStatus = msg.error_code;
- resultExtras = r.resultExtras;
- bool logicalDeviceResultError = false;
- if (hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_RESULT ==
- errorCode) {
- if (physicalCameraId.size() > 0) {
- String8 cameraId(physicalCameraId);
- auto iter = r.physicalCameraIds.find(cameraId);
- if (iter == r.physicalCameraIds.end()) {
- ALOGE("%s: Reported result failure for physical camera device: %s "
- " which is not part of the respective request!",
- __FUNCTION__, cameraId.string());
- break;
- }
- r.physicalCameraIds.erase(iter);
- resultExtras.errorPhysicalCameraId = physicalCameraId;
- } else {
- logicalDeviceResultError = true;
- }
- }
-
- if (logicalDeviceResultError
- || hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST ==
- errorCode) {
- r.skipResultMetadata = true;
- }
- if (logicalDeviceResultError) {
- // In case of missing result check whether the buffers
- // returned. If they returned, then remove inflight
- // request.
- // TODO: should we call this for ERROR_CAMERA_REQUEST as well?
- // otherwise we are depending on HAL to send the buffers back after
- // calling notifyError. Not sure if that's in the spec.
- removeInFlightRequestIfReadyLocked(idx);
- }
- } else {
- resultExtras.frameNumber = msg.frame_number;
- ALOGE("Camera %s: %s: cannot find in-flight request on "
- "frame %" PRId64 " error", mId.string(), __FUNCTION__,
- resultExtras.frameNumber);
- }
- }
- resultExtras.errorStreamId = streamId;
- if (listener != NULL) {
- listener->notifyError(errorCode, resultExtras);
- } else {
- ALOGE("Camera %s: %s: no listener available", mId.string(), __FUNCTION__);
- }
- break;
- default:
- // SET_ERR calls notifyError
- SET_ERR("Unknown error message from HAL: %d", msg.error_code);
- break;
- }
-}
-
-void Camera3Device::notifyShutter(const camera3_shutter_msg_t &msg,
- sp<NotificationListener> listener) {
- ATRACE_CALL();
- ssize_t idx;
-
- // Set timestamp for the request in the in-flight tracking
- // and get the request ID to send upstream
- {
- Mutex::Autolock l(mInFlightLock);
- idx = mInFlightMap.indexOfKey(msg.frame_number);
- if (idx >= 0) {
- InFlightRequest &r = mInFlightMap.editValueAt(idx);
-
- // Verify ordering of shutter notifications
- {
- Mutex::Autolock l(mOutputLock);
- // TODO: need to track errors for tighter bounds on expected frame number.
- if (r.hasInputBuffer) {
- if (msg.frame_number < mNextReprocessShutterFrameNumber) {
- SET_ERR("Reprocess shutter notification out-of-order. Expected "
- "notification for frame %d, got frame %d",
- mNextReprocessShutterFrameNumber, msg.frame_number);
- return;
- }
- mNextReprocessShutterFrameNumber = msg.frame_number + 1;
- } else if (r.zslCapture && r.stillCapture) {
- if (msg.frame_number < mNextZslStillShutterFrameNumber) {
- SET_ERR("ZSL still capture shutter notification out-of-order. Expected "
- "notification for frame %d, got frame %d",
- mNextZslStillShutterFrameNumber, msg.frame_number);
- return;
- }
- mNextZslStillShutterFrameNumber = msg.frame_number + 1;
- } else {
- if (msg.frame_number < mNextShutterFrameNumber) {
- SET_ERR("Shutter notification out-of-order. Expected "
- "notification for frame %d, got frame %d",
- mNextShutterFrameNumber, msg.frame_number);
- return;
- }
- mNextShutterFrameNumber = msg.frame_number + 1;
- }
- }
-
- r.shutterTimestamp = msg.timestamp;
- if (r.hasCallback) {
- ALOGVV("Camera %s: %s: Shutter fired for frame %d (id %d) at %" PRId64,
- mId.string(), __FUNCTION__,
- msg.frame_number, r.resultExtras.requestId, msg.timestamp);
- // Call listener, if any
- if (listener != NULL) {
- listener->notifyShutter(r.resultExtras, msg.timestamp);
- }
- // send pending result and buffers
- sendCaptureResult(r.pendingMetadata, r.resultExtras,
- r.collectedPartialResult, msg.frame_number,
- r.hasInputBuffer, r.zslCapture && r.stillCapture,
- r.physicalMetadatas);
- }
- bool timestampIncreasing = !(r.zslCapture || r.hasInputBuffer);
- returnOutputBuffers(r.pendingOutputBuffers.array(),
- r.pendingOutputBuffers.size(), r.shutterTimestamp, timestampIncreasing,
- r.outputSurfaces, r.resultExtras);
- r.pendingOutputBuffers.clear();
-
- removeInFlightRequestIfReadyLocked(idx);
- }
- }
- if (idx < 0) {
- SET_ERR("Shutter notification for non-existent frame number %d",
- msg.frame_number);
- }
+ camera3::flushInflightRequests(states);
}
CameraMetadata Camera3Device::getLatestRequestLocked() {
@@ -4029,7 +2833,6 @@
return retVal;
}
-
void Camera3Device::monitorMetadata(TagMonitor::eventSource source,
int64_t frameNumber, nsecs_t timestamp, const CameraMetadata& metadata,
const std::unordered_map<std::string, CameraMetadata>& physicalMetadata) {
@@ -4045,13 +2848,18 @@
Camera3Device::HalInterface::HalInterface(
sp<ICameraDeviceSession> &session,
std::shared_ptr<RequestMetadataQueue> queue,
- bool useHalBufManager) :
+ bool useHalBufManager, bool supportOfflineProcessing) :
mHidlSession(session),
mRequestMetadataQueue(queue),
mUseHalBufManager(useHalBufManager),
- mIsReconfigurationQuerySupported(true) {
+ mIsReconfigurationQuerySupported(true),
+ mSupportOfflineProcessing(supportOfflineProcessing) {
// Check with hardware service manager if we can downcast these interfaces
// Somewhat expensive, so cache the results at startup
+ auto castResult_3_6 = device::V3_6::ICameraDeviceSession::castFrom(mHidlSession);
+ if (castResult_3_6.isOk()) {
+ mHidlSession_3_6 = castResult_3_6;
+ }
auto castResult_3_5 = device::V3_5::ICameraDeviceSession::castFrom(mHidlSession);
if (castResult_3_5.isOk()) {
mHidlSession_3_5 = castResult_3_5;
@@ -4066,18 +2874,22 @@
}
}
-Camera3Device::HalInterface::HalInterface() : mUseHalBufManager(false) {}
+Camera3Device::HalInterface::HalInterface() :
+ mUseHalBufManager(false),
+ mSupportOfflineProcessing(false) {}
Camera3Device::HalInterface::HalInterface(const HalInterface& other) :
mHidlSession(other.mHidlSession),
mRequestMetadataQueue(other.mRequestMetadataQueue),
- mUseHalBufManager(other.mUseHalBufManager) {}
+ mUseHalBufManager(other.mUseHalBufManager),
+ mSupportOfflineProcessing(other.mSupportOfflineProcessing) {}
bool Camera3Device::HalInterface::valid() {
return (mHidlSession != nullptr);
}
void Camera3Device::HalInterface::clear() {
+ mHidlSession_3_6.clear();
mHidlSession_3_5.clear();
mHidlSession_3_4.clear();
mHidlSession_3_3.clear();
@@ -4255,20 +3067,10 @@
activeStreams.insert(streamId);
// Create Buffer ID map if necessary
- if (mBufferIdMaps.count(streamId) == 0) {
- mBufferIdMaps.emplace(streamId, BufferIdMap{});
- }
+ mBufferRecords.tryCreateBufferCache(streamId);
}
// remove BufferIdMap for deleted streams
- for(auto it = mBufferIdMaps.begin(); it != mBufferIdMaps.end();) {
- int streamId = it->first;
- bool active = activeStreams.count(streamId) > 0;
- if (!active) {
- it = mBufferIdMaps.erase(it);
- } else {
- ++it;
- }
- }
+ mBufferRecords.removeInactiveBufferCaches(activeStreams);
StreamConfigurationMode operationMode;
res = mapToStreamConfigurationMode(
@@ -4286,6 +3088,7 @@
// Invoke configureStreams
device::V3_3::HalStreamConfiguration finalConfiguration;
device::V3_4::HalStreamConfiguration finalConfiguration3_4;
+ device::V3_6::HalStreamConfiguration finalConfiguration3_6;
common::V1_0::Status status;
auto configStream34Cb = [&status, &finalConfiguration3_4]
@@ -4294,6 +3097,12 @@
status = s;
};
+ auto configStream36Cb = [&status, &finalConfiguration3_6]
+ (common::V1_0::Status s, const device::V3_6::HalStreamConfiguration& halConfiguration) {
+ finalConfiguration3_6 = halConfiguration;
+ status = s;
+ };
+
auto postprocConfigStream34 = [&finalConfiguration, &finalConfiguration3_4]
(hardware::Return<void>& err) -> status_t {
if (!err.isOk()) {
@@ -4307,8 +3116,32 @@
return OK;
};
+ auto postprocConfigStream36 = [&finalConfiguration, &finalConfiguration3_6]
+ (hardware::Return<void>& err) -> status_t {
+ if (!err.isOk()) {
+ ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
+ return DEAD_OBJECT;
+ }
+ finalConfiguration.streams.resize(finalConfiguration3_6.streams.size());
+ for (size_t i = 0; i < finalConfiguration3_6.streams.size(); i++) {
+ finalConfiguration.streams[i] = finalConfiguration3_6.streams[i].v3_4.v3_3;
+ }
+ return OK;
+ };
+
// See which version of HAL we have
- if (mHidlSession_3_5 != nullptr) {
+ if (mHidlSession_3_6 != nullptr) {
+ ALOGV("%s: v3.6 device found", __FUNCTION__);
+ device::V3_5::StreamConfiguration requestedConfiguration3_5;
+ requestedConfiguration3_5.v3_4 = requestedConfiguration3_4;
+ requestedConfiguration3_5.streamConfigCounter = mNextStreamConfigCounter++;
+ auto err = mHidlSession_3_6->configureStreams_3_6(
+ requestedConfiguration3_5, configStream36Cb);
+ res = postprocConfigStream36(err);
+ if (res != OK) {
+ return res;
+ }
+ } else if (mHidlSession_3_5 != nullptr) {
ALOGV("%s: v3.5 device found", __FUNCTION__);
device::V3_5::StreamConfiguration requestedConfiguration3_5;
requestedConfiguration3_5.v3_4 = requestedConfiguration3_4;
@@ -4390,11 +3223,16 @@
return INVALID_OPERATION;
}
device::V3_3::HalStream &src = finalConfiguration.streams[realIdx];
+ device::V3_6::HalStream &src_36 = finalConfiguration3_6.streams[realIdx];
Camera3Stream* dstStream = Camera3Stream::cast(dst);
int overrideFormat = mapToFrameworkFormat(src.v3_2.overrideFormat);
android_dataspace overrideDataSpace = mapToFrameworkDataspace(src.overrideDataSpace);
+ if (mHidlSession_3_6 != nullptr) {
+ dstStream->setOfflineProcessingSupport(src_36.supportOffline);
+ }
+
if (dstStream->getOriginalFormat() != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
dstStream->setFormatOverride(false);
dstStream->setDataSpaceOverride(false);
@@ -4458,7 +3296,6 @@
captureRequest->fmqSettingsSize = 0;
{
- std::lock_guard<std::mutex> lock(mInflightLock);
if (request->input_buffer != nullptr) {
int32_t streamId = Camera3Stream::cast(request->input_buffer->stream)->getId();
buffer_handle_t buf = *(request->input_buffer->buffer);
@@ -4478,7 +3315,7 @@
captureRequest->inputBuffer.acquireFence = acquireFence;
captureRequest->inputBuffer.releaseFence = nullptr;
- pushInflightBufferLocked(captureRequest->frameNumber, streamId,
+ mBufferRecords.pushInflightBuffer(captureRequest->frameNumber, streamId,
request->input_buffer->buffer);
inflightBuffers->push_back(std::make_pair(captureRequest->frameNumber, streamId));
} else {
@@ -4519,7 +3356,8 @@
// Output buffers are empty when using HAL buffer manager
if (!mUseHalBufManager) {
- pushInflightBufferLocked(captureRequest->frameNumber, streamId, src->buffer);
+ mBufferRecords.pushInflightBuffer(
+ captureRequest->frameNumber, streamId, src->buffer);
inflightBuffers->push_back(std::make_pair(captureRequest->frameNumber, streamId));
}
}
@@ -4576,7 +3414,7 @@
/*out*/&handlesCreated, /*out*/&inflightBuffers);
}
if (res != OK) {
- popInflightBuffers(inflightBuffers);
+ mBufferRecords.popInflightBuffers(inflightBuffers);
cleanupNativeHandles(&handlesCreated);
return res;
}
@@ -4584,10 +3422,10 @@
std::vector<device::V3_2::BufferCache> cachesToRemove;
{
- std::lock_guard<std::mutex> lock(mBufferIdMapLock);
+ std::lock_guard<std::mutex> lock(mFreedBuffersLock);
for (auto& pair : mFreedBuffers) {
// The stream might have been removed since onBufferFreed
- if (mBufferIdMaps.find(pair.first) != mBufferIdMaps.end()) {
+ if (mBufferRecords.isStreamCached(pair.first)) {
cachesToRemove.push_back({pair.first, pair.second});
}
}
@@ -4694,7 +3532,7 @@
cleanupNativeHandles(&handlesCreated);
}
} else {
- popInflightBuffers(inflightBuffers);
+ mBufferRecords.popInflightBuffers(inflightBuffers);
cleanupNativeHandles(&handlesCreated);
}
return res;
@@ -4753,72 +3591,94 @@
}
}
+status_t Camera3Device::HalInterface::switchToOffline(
+ const std::vector<int32_t>& streamsToKeep,
+ /*out*/hardware::camera::device::V3_6::CameraOfflineSessionInfo* offlineSessionInfo,
+ /*out*/sp<hardware::camera::device::V3_6::ICameraOfflineSession>* offlineSession,
+ /*out*/camera3::BufferRecords* bufferRecords) {
+ ATRACE_NAME("CameraHal::switchToOffline");
+ if (!valid() || mHidlSession_3_6 == nullptr) {
+ ALOGE("%s called on invalid camera!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ if (offlineSessionInfo == nullptr || offlineSession == nullptr || bufferRecords == nullptr) {
+ ALOGE("%s: output arguments must not be null!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ common::V1_0::Status status = common::V1_0::Status::INTERNAL_ERROR;
+ auto resultCallback =
+ [&status, &offlineSessionInfo, &offlineSession] (auto s, auto info, auto session) {
+ status = s;
+ *offlineSessionInfo = info;
+ *offlineSession = session;
+ };
+ auto err = mHidlSession_3_6->switchToOffline(streamsToKeep, resultCallback);
+
+ if (!err.isOk()) {
+ ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
+ return DEAD_OBJECT;
+ }
+
+ status_t ret = CameraProviderManager::mapToStatusT(status);
+ if (ret != OK) {
+ return ret;
+ }
+
+ // TODO: assert no ongoing requestBuffer/returnBuffer call here
+ // TODO: update RequestBufferStateMachine to block requestBuffer/returnBuffer once HAL
+ // returns from switchToOffline.
+
+
+ // Validate buffer caches
+ std::vector<int32_t> streams;
+ streams.reserve(offlineSessionInfo->offlineStreams.size());
+ for (auto offlineStream : offlineSessionInfo->offlineStreams) {
+ int32_t id = offlineStream.id;
+ streams.push_back(id);
+ // Verify buffer caches
+ std::vector<uint64_t> bufIds(offlineStream.circulatingBufferIds.begin(),
+ offlineStream.circulatingBufferIds.end());
+ if (!verifyBufferIds(id, bufIds)) {
+ ALOGE("%s: stream ID %d buffer cache records mismatch!", __FUNCTION__, id);
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ // Move buffer records
+ bufferRecords->takeBufferCaches(mBufferRecords, streams);
+ bufferRecords->takeInflightBufferMap(mBufferRecords);
+ bufferRecords->takeRequestedBufferMap(mBufferRecords);
+ return ret;
+}
+
void Camera3Device::HalInterface::getInflightBufferKeys(
std::vector<std::pair<int32_t, int32_t>>* out) {
- std::lock_guard<std::mutex> lock(mInflightLock);
- out->clear();
- out->reserve(mInflightBufferMap.size());
- for (auto& pair : mInflightBufferMap) {
- uint64_t key = pair.first;
- int32_t streamId = key & 0xFFFFFFFF;
- int32_t frameNumber = (key >> 32) & 0xFFFFFFFF;
- out->push_back(std::make_pair(frameNumber, streamId));
- }
+ mBufferRecords.getInflightBufferKeys(out);
return;
}
void Camera3Device::HalInterface::getInflightRequestBufferKeys(
std::vector<uint64_t>* out) {
- std::lock_guard<std::mutex> lock(mRequestedBuffersLock);
- out->clear();
- out->reserve(mRequestedBuffers.size());
- for (auto& pair : mRequestedBuffers) {
- out->push_back(pair.first);
- }
+ mBufferRecords.getInflightRequestBufferKeys(out);
return;
}
-status_t Camera3Device::HalInterface::pushInflightBufferLocked(
- int32_t frameNumber, int32_t streamId, buffer_handle_t *buffer) {
- uint64_t key = static_cast<uint64_t>(frameNumber) << 32 | static_cast<uint64_t>(streamId);
- mInflightBufferMap[key] = buffer;
- return OK;
+bool Camera3Device::HalInterface::verifyBufferIds(
+ int32_t streamId, std::vector<uint64_t>& bufIds) {
+ return mBufferRecords.verifyBufferIds(streamId, bufIds);
}
status_t Camera3Device::HalInterface::popInflightBuffer(
int32_t frameNumber, int32_t streamId,
/*out*/ buffer_handle_t **buffer) {
- std::lock_guard<std::mutex> lock(mInflightLock);
-
- uint64_t key = static_cast<uint64_t>(frameNumber) << 32 | static_cast<uint64_t>(streamId);
- auto it = mInflightBufferMap.find(key);
- if (it == mInflightBufferMap.end()) return NAME_NOT_FOUND;
- if (buffer != nullptr) {
- *buffer = it->second;
- }
- mInflightBufferMap.erase(it);
- return OK;
-}
-
-void Camera3Device::HalInterface::popInflightBuffers(
- const std::vector<std::pair<int32_t, int32_t>>& buffers) {
- for (const auto& pair : buffers) {
- int32_t frameNumber = pair.first;
- int32_t streamId = pair.second;
- popInflightBuffer(frameNumber, streamId, nullptr);
- }
+ return mBufferRecords.popInflightBuffer(frameNumber, streamId, buffer);
}
status_t Camera3Device::HalInterface::pushInflightRequestBuffer(
uint64_t bufferId, buffer_handle_t* buf, int32_t streamId) {
- std::lock_guard<std::mutex> lock(mRequestedBuffersLock);
- auto pair = mRequestedBuffers.insert({bufferId, {streamId, buf}});
- if (!pair.second) {
- ALOGE("%s: bufId %" PRIu64 " is already inflight!",
- __FUNCTION__, bufferId);
- return BAD_VALUE;
- }
- return OK;
+ return mBufferRecords.pushInflightRequestBuffer(bufferId, buf, streamId);
}
// Find and pop a buffer_handle_t based on bufferId
@@ -4826,81 +3686,29 @@
uint64_t bufferId,
/*out*/ buffer_handle_t** buffer,
/*optional out*/ int32_t* streamId) {
- if (buffer == nullptr) {
- ALOGE("%s: buffer (%p) must not be null", __FUNCTION__, buffer);
- return BAD_VALUE;
- }
- std::lock_guard<std::mutex> lock(mRequestedBuffersLock);
- auto it = mRequestedBuffers.find(bufferId);
- if (it == mRequestedBuffers.end()) {
- ALOGE("%s: bufId %" PRIu64 " is not inflight!",
- __FUNCTION__, bufferId);
- return BAD_VALUE;
- }
- *buffer = it->second.second;
- if (streamId != nullptr) {
- *streamId = it->second.first;
- }
- mRequestedBuffers.erase(it);
- return OK;
+ return mBufferRecords.popInflightRequestBuffer(bufferId, buffer, streamId);
}
std::pair<bool, uint64_t> Camera3Device::HalInterface::getBufferId(
const buffer_handle_t& buf, int streamId) {
- std::lock_guard<std::mutex> lock(mBufferIdMapLock);
-
- BufferIdMap& bIdMap = mBufferIdMaps.at(streamId);
- auto it = bIdMap.find(buf);
- if (it == bIdMap.end()) {
- bIdMap[buf] = mNextBufferId++;
- ALOGV("stream %d now have %zu buffer caches, buf %p",
- streamId, bIdMap.size(), buf);
- return std::make_pair(true, mNextBufferId - 1);
- } else {
- return std::make_pair(false, it->second);
- }
+ return mBufferRecords.getBufferId(buf, streamId);
}
void Camera3Device::HalInterface::onBufferFreed(
int streamId, const native_handle_t* handle) {
- std::lock_guard<std::mutex> lock(mBufferIdMapLock);
- uint64_t bufferId = BUFFER_ID_NO_BUFFER;
- auto mapIt = mBufferIdMaps.find(streamId);
- if (mapIt == mBufferIdMaps.end()) {
- // streamId might be from a deleted stream here
- ALOGI("%s: stream %d has been removed",
- __FUNCTION__, streamId);
- return;
+ uint32_t bufferId = mBufferRecords.removeOneBufferCache(streamId, handle);
+ std::lock_guard<std::mutex> lock(mFreedBuffersLock);
+ if (bufferId != BUFFER_ID_NO_BUFFER) {
+ mFreedBuffers.push_back(std::make_pair(streamId, bufferId));
}
- BufferIdMap& bIdMap = mapIt->second;
- auto it = bIdMap.find(handle);
- if (it == bIdMap.end()) {
- ALOGW("%s: cannot find buffer %p in stream %d",
- __FUNCTION__, handle, streamId);
- return;
- } else {
- bufferId = it->second;
- bIdMap.erase(it);
- ALOGV("%s: stream %d now have %zu buffer caches after removing buf %p",
- __FUNCTION__, streamId, bIdMap.size(), handle);
- }
- mFreedBuffers.push_back(std::make_pair(streamId, bufferId));
}
void Camera3Device::HalInterface::onStreamReConfigured(int streamId) {
- std::lock_guard<std::mutex> lock(mBufferIdMapLock);
- auto mapIt = mBufferIdMaps.find(streamId);
- if (mapIt == mBufferIdMaps.end()) {
- ALOGE("%s: streamId %d not found!", __FUNCTION__, streamId);
- return;
- }
-
- BufferIdMap& bIdMap = mapIt->second;
- for (const auto& it : bIdMap) {
- uint64_t bufferId = it.second;
+ std::vector<uint64_t> bufIds = mBufferRecords.clearBufferCaches(streamId);
+ std::lock_guard<std::mutex> lock(mFreedBuffersLock);
+ for (auto bufferId : bufIds) {
mFreedBuffers.push_back(std::make_pair(streamId, bufferId));
}
- bIdMap.clear();
}
/**
@@ -4925,6 +3733,7 @@
mLatestRequestId(NAME_NOT_FOUND),
mCurrentAfTriggerId(0),
mCurrentPreCaptureTriggerId(0),
+ mRotateAndCropOverride(ANDROID_SCALER_ROTATE_AND_CROP_NONE),
mRepeatingLastFrameNumber(
hardware::camera2::ICameraDeviceUser::NO_IN_FLIGHT_REPEATING_FRAMES),
mPrepareVideoStream(false),
@@ -5396,6 +4205,9 @@
bool Camera3Device::RequestThread::threadLoop() {
ATRACE_CALL();
status_t res;
+ // Any function called from threadLoop() must not hold mInterfaceLock since
+ // it could lead to deadlocks (disconnect() -> hold mInterfaceMutex -> wait for request thread
+ // to finish -> request thread waits on mInterfaceMutex) http://b/143513518
// Handle paused state.
if (waitIfPaused()) {
@@ -5524,6 +4336,7 @@
Mutex::Autolock l(mRequestLock);
mNextRequests.clear();
}
+ mRequestSubmittedSignal.signal();
return submitRequestSuccess;
}
@@ -5554,12 +4367,16 @@
bool triggersMixedIn = (triggerCount > 0 || mPrevTriggers > 0);
mPrevTriggers = triggerCount;
+ bool rotateAndCropChanged = overrideAutoRotateAndCrop(captureRequest);
+
// If the request is the same as last, or we had triggers last time
- bool newRequest = (mPrevRequest != captureRequest || triggersMixedIn) &&
+ bool newRequest =
+ (mPrevRequest != captureRequest || triggersMixedIn || rotateAndCropChanged) &&
// Request settings are all the same within one batch, so only treat the first
// request in a batch as new
!(batchedRequest && i > 0);
if (newRequest) {
+ std::set<std::string> cameraIdsWithZoom;
/**
* HAL workaround:
* Insert a dummy trigger ID if a trigger is set but no trigger ID is
@@ -5592,6 +4409,43 @@
return INVALID_OPERATION;
}
}
+
+ for (it = captureRequest->mSettingsList.begin();
+ it != captureRequest->mSettingsList.end(); it++) {
+ if (parent->mZoomRatioMappers.find(it->cameraId) ==
+ parent->mZoomRatioMappers.end()) {
+ continue;
+ }
+
+ camera_metadata_entry_t e = it->metadata.find(ANDROID_CONTROL_ZOOM_RATIO);
+ if (e.count > 0 && e.data.f[0] != 1.0f) {
+ cameraIdsWithZoom.insert(it->cameraId);
+ }
+
+ res = parent->mZoomRatioMappers[it->cameraId].updateCaptureRequest(
+ &(it->metadata));
+ if (res != OK) {
+ SET_ERR("RequestThread: Unable to correct capture requests "
+ "for zoom ratio for request %d: %s (%d)",
+ halRequest->frame_number, strerror(-res), res);
+ return INVALID_OPERATION;
+ }
+ }
+ if (captureRequest->mRotateAndCropAuto) {
+ for (it = captureRequest->mSettingsList.begin();
+ it != captureRequest->mSettingsList.end(); it++) {
+ auto mapper = parent->mRotateAndCropMappers.find(it->cameraId);
+ if (mapper != parent->mRotateAndCropMappers.end()) {
+ res = mapper->second.updateCaptureRequest(&(it->metadata));
+ if (res != OK) {
+ SET_ERR("RequestThread: Unable to correct capture requests "
+ "for rotate-and-crop for request %d: %s (%d)",
+ halRequest->frame_number, strerror(-res), res);
+ return INVALID_OPERATION;
+ }
+ }
+ }
+ }
}
}
@@ -5602,6 +4456,7 @@
captureRequest->mSettingsList.begin()->metadata.sort();
halRequest->settings = captureRequest->mSettingsList.begin()->metadata.getAndLock();
mPrevRequest = captureRequest;
+ mPrevCameraIdsWithZoom = cameraIdsWithZoom;
ALOGVV("%s: Request settings are NEW", __FUNCTION__);
IF_ALOGV() {
@@ -5790,6 +4645,7 @@
hasCallback,
calculateMaxExpectedDuration(halRequest->settings),
requestedPhysicalCameras, isStillCapture, isZslCapture,
+ captureRequest->mRotateAndCropAuto, mPrevCameraIdsWithZoom,
(mUseHalBufManager) ? uniqueSurfaceIdMap :
SurfaceMap{});
ALOGVV("%s: registered in flight requestId = %" PRId32 ", frameNumber = %" PRId64
@@ -5903,9 +4759,52 @@
mStreamIdsToBeDrained = streamIds;
}
+status_t Camera3Device::RequestThread::switchToOffline(
+ const std::vector<int32_t>& streamsToKeep,
+ /*out*/hardware::camera::device::V3_6::CameraOfflineSessionInfo* offlineSessionInfo,
+ /*out*/sp<hardware::camera::device::V3_6::ICameraOfflineSession>* offlineSession,
+ /*out*/camera3::BufferRecords* bufferRecords) {
+ Mutex::Autolock l(mRequestLock);
+ clearRepeatingRequestsLocked(/*lastFrameNumber*/nullptr);
+
+ // Wait until request thread is fully stopped
+ // TBD: check if request thread is being paused by other APIs (shouldn't be)
+
+ // We could also check for mRepeatingRequests.empty(), but the API interface
+ // is serialized by Camera3Device::mInterfaceLock so no one should be able to submit any
+ // new requests during the call; hence skip that check.
+ bool queueEmpty = mNextRequests.empty() && mRequestQueue.empty();
+ while (!queueEmpty) {
+ status_t res = mRequestSubmittedSignal.waitRelative(mRequestLock, kRequestSubmitTimeout);
+ if (res == TIMED_OUT) {
+ ALOGE("%s: request thread failed to submit one request within timeout!", __FUNCTION__);
+ return res;
+ } else if (res != OK) {
+ ALOGE("%s: request thread failed to submit a request: %s (%d)!",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+ queueEmpty = mNextRequests.empty() && mRequestQueue.empty();
+ }
+
+ return mInterface->switchToOffline(
+ streamsToKeep, offlineSessionInfo, offlineSession, bufferRecords);
+}
+
+status_t Camera3Device::RequestThread::setRotateAndCropAutoBehavior(
+ camera_metadata_enum_android_scaler_rotate_and_crop_t rotateAndCropValue) {
+ ATRACE_CALL();
+ Mutex::Autolock l(mTriggerMutex);
+ if (rotateAndCropValue == ANDROID_SCALER_ROTATE_AND_CROP_AUTO) {
+ return BAD_VALUE;
+ }
+ mRotateAndCropOverride = rotateAndCropValue;
+ return OK;
+}
+
nsecs_t Camera3Device::getExpectedInFlightDuration() {
ATRACE_CALL();
- Mutex::Autolock al(mInFlightLock);
+ std::lock_guard<std::mutex> l(mInFlightLock);
return mExpectedInflightDuration > kMinInflightDuration ?
mExpectedInflightDuration : kMinInflightDuration;
}
@@ -5991,7 +4890,7 @@
{
sp<Camera3Device> parent = mParent.promote();
if (parent != NULL) {
- Mutex::Autolock l(parent->mInFlightLock);
+ std::lock_guard<std::mutex> l(parent->mInFlightLock);
ssize_t idx = parent->mInFlightMap.indexOfKey(captureRequest->mResultExtras.frameNumber);
if (idx >= 0) {
ALOGV("%s: Remove inflight request from queue: frameNumber %" PRId64,
@@ -6418,6 +5317,32 @@
return OK;
}
+bool Camera3Device::RequestThread::overrideAutoRotateAndCrop(
+ const sp<CaptureRequest> &request) {
+ ATRACE_CALL();
+
+ if (request->mRotateAndCropAuto) {
+ Mutex::Autolock l(mTriggerMutex);
+ CameraMetadata &metadata = request->mSettingsList.begin()->metadata;
+
+ auto rotateAndCropEntry = metadata.find(ANDROID_SCALER_ROTATE_AND_CROP);
+ if (rotateAndCropEntry.count > 0) {
+ if (rotateAndCropEntry.data.u8[0] == mRotateAndCropOverride) {
+ return false;
+ } else {
+ rotateAndCropEntry.data.u8[0] = mRotateAndCropOverride;
+ return true;
+ }
+ } else {
+ uint8_t rotateAndCrop_u8 = mRotateAndCropOverride;
+ metadata.update(ANDROID_SCALER_ROTATE_AND_CROP,
+ &rotateAndCrop_u8, 1);
+ return true;
+ }
+ }
+ return false;
+}
+
/**
* PreparerThread inner class methods
*/
@@ -6680,6 +5605,7 @@
void Camera3Device::RequestBufferStateMachine::onStreamsConfigured() {
std::lock_guard<std::mutex> lock(mLock);
+ mSwitchedToOffline = false;
mStatus = RB_STATUS_READY;
return;
}
@@ -6722,6 +5648,20 @@
return;
}
+bool Camera3Device::RequestBufferStateMachine::onSwitchToOfflineSuccess() {
+ std::lock_guard<std::mutex> lock(mLock);
+ if (mRequestBufferOngoing) {
+ ALOGE("%s: HAL must not be requesting buffer after HAL returns switchToOffline!",
+ __FUNCTION__);
+ return false;
+ }
+ mSwitchedToOffline = true;
+ mInflightMapEmpty = true;
+ mRequestThreadPaused = true;
+ mStatus = RB_STATUS_STOPPED;
+ return true;
+}
+
void Camera3Device::RequestBufferStateMachine::notifyTrackerLocked(bool active) {
sp<StatusTracker> statusTracker = mStatusTracker.promote();
if (statusTracker != nullptr) {
@@ -6741,75 +5681,288 @@
return false;
}
-status_t Camera3Device::fixupMonochromeTags(const CameraMetadata& deviceInfo,
- CameraMetadata& resultMetadata) {
- status_t res = OK;
- if (!mNeedFixupMonochromeTags) {
- return res;
+bool Camera3Device::startRequestBuffer() {
+ return mRequestBufferSM.startRequestBuffer();
+}
+
+void Camera3Device::endRequestBuffer() {
+ mRequestBufferSM.endRequestBuffer();
+}
+
+nsecs_t Camera3Device::getWaitDuration() {
+ return kBaseGetBufferWait + getExpectedInFlightDuration();
+}
+
+void Camera3Device::getInflightBufferKeys(std::vector<std::pair<int32_t, int32_t>>* out) {
+ mInterface->getInflightBufferKeys(out);
+}
+
+void Camera3Device::getInflightRequestBufferKeys(std::vector<uint64_t>* out) {
+ mInterface->getInflightRequestBufferKeys(out);
+}
+
+std::vector<sp<Camera3StreamInterface>> Camera3Device::getAllStreams() {
+ std::vector<sp<Camera3StreamInterface>> ret;
+ bool hasInputStream = mInputStream != nullptr;
+ ret.reserve(mOutputStreams.size() + mDeletedStreams.size() + ((hasInputStream) ? 1 : 0));
+ if (hasInputStream) {
+ ret.push_back(mInputStream);
+ }
+ for (size_t i = 0; i < mOutputStreams.size(); i++) {
+ ret.push_back(mOutputStreams[i]);
+ }
+ for (size_t i = 0; i < mDeletedStreams.size(); i++) {
+ ret.push_back(mDeletedStreams[i]);
+ }
+ return ret;
+}
+
+status_t Camera3Device::switchToOffline(
+ const std::vector<int32_t>& streamsToKeep,
+ /*out*/ sp<CameraOfflineSessionBase>* session) {
+ ATRACE_CALL();
+ if (session == nullptr) {
+ ALOGE("%s: session must not be null", __FUNCTION__);
+ return BAD_VALUE;
}
- // Remove tags that are not applicable to monochrome camera.
- int32_t tagsToRemove[] = {
- ANDROID_SENSOR_GREEN_SPLIT,
- ANDROID_SENSOR_NEUTRAL_COLOR_POINT,
- ANDROID_COLOR_CORRECTION_MODE,
- ANDROID_COLOR_CORRECTION_TRANSFORM,
- ANDROID_COLOR_CORRECTION_GAINS,
- };
- for (auto tag : tagsToRemove) {
- res = resultMetadata.erase(tag);
- if (res != OK) {
- ALOGE("%s: Failed to remove tag %d for monochrome camera", __FUNCTION__, tag);
- return res;
+ Mutex::Autolock il(mInterfaceLock);
+
+ bool hasInputStream = mInputStream != nullptr;
+ int32_t inputStreamId = hasInputStream ? mInputStream->getId() : -1;
+ bool inputStreamSupportsOffline = hasInputStream ?
+ mInputStream->getOfflineProcessingSupport() : false;
+ auto outputStreamIds = mOutputStreams.getStreamIds();
+ auto streamIds = outputStreamIds;
+ if (hasInputStream) {
+ streamIds.push_back(mInputStream->getId());
+ }
+
+ // Check all streams in streamsToKeep supports offline mode
+ for (auto id : streamsToKeep) {
+ if (std::find(streamIds.begin(), streamIds.end(), id) == streamIds.end()) {
+ ALOGE("%s: Unknown stream ID %d", __FUNCTION__, id);
+ return BAD_VALUE;
+ } else if (id == inputStreamId) {
+ if (!inputStreamSupportsOffline) {
+ ALOGE("%s: input stream %d cannot be switched to offline",
+ __FUNCTION__, id);
+ return BAD_VALUE;
+ }
+ } else {
+ sp<camera3::Camera3OutputStreamInterface> stream = mOutputStreams.get(id);
+ if (!stream->getOfflineProcessingSupport()) {
+ ALOGE("%s: output stream %d cannot be switched to offline",
+ __FUNCTION__, id);
+ return BAD_VALUE;
+ }
}
}
- // ANDROID_SENSOR_DYNAMIC_BLACK_LEVEL
- camera_metadata_entry blEntry = resultMetadata.find(ANDROID_SENSOR_DYNAMIC_BLACK_LEVEL);
- for (size_t i = 1; i < blEntry.count; i++) {
- blEntry.data.f[i] = blEntry.data.f[0];
+ // TODO: block surface sharing and surface group streams until we can support them
+
+ // Stop repeating request, wait until all remaining requests are submitted, then call into
+ // HAL switchToOffline
+ hardware::camera::device::V3_6::CameraOfflineSessionInfo offlineSessionInfo;
+ sp<hardware::camera::device::V3_6::ICameraOfflineSession> offlineSession;
+ camera3::BufferRecords bufferRecords;
+ status_t ret = mRequestThread->switchToOffline(
+ streamsToKeep, &offlineSessionInfo, &offlineSession, &bufferRecords);
+
+ if (ret != OK) {
+ SET_ERR("Switch to offline failed: %s (%d)", strerror(-ret), ret);
+ return ret;
}
- // ANDROID_SENSOR_NOISE_PROFILE
- camera_metadata_entry npEntry = resultMetadata.find(ANDROID_SENSOR_NOISE_PROFILE);
- if (npEntry.count > 0 && npEntry.count % 2 == 0) {
- double np[] = {npEntry.data.d[0], npEntry.data.d[1]};
- res = resultMetadata.update(ANDROID_SENSOR_NOISE_PROFILE, np, 2);
- if (res != OK) {
- ALOGE("%s: Failed to update SENSOR_NOISE_PROFILE: %s (%d)",
- __FUNCTION__, strerror(-res), res);
- return res;
+ bool succ = mRequestBufferSM.onSwitchToOfflineSuccess();
+ if (!succ) {
+ SET_ERR("HAL must not be calling requestStreamBuffers call");
+ // TODO: block ALL callbacks from HAL till app configured new streams?
+ return UNKNOWN_ERROR;
+ }
+
+ // Verify offlineSessionInfo
+ std::vector<int32_t> offlineStreamIds;
+ offlineStreamIds.reserve(offlineSessionInfo.offlineStreams.size());
+ for (auto offlineStream : offlineSessionInfo.offlineStreams) {
+ // verify stream IDs
+ int32_t id = offlineStream.id;
+ if (std::find(streamIds.begin(), streamIds.end(), id) == streamIds.end()) {
+ SET_ERR("stream ID %d not found!", id);
+ return UNKNOWN_ERROR;
+ }
+
+ // When not using HAL buf manager, only allow streams requested by app to be preserved
+ if (!mUseHalBufManager) {
+ if (std::find(streamsToKeep.begin(), streamsToKeep.end(), id) == streamsToKeep.end()) {
+ SET_ERR("stream ID %d must not be switched to offline!", id);
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ offlineStreamIds.push_back(id);
+ sp<Camera3StreamInterface> stream = (id == inputStreamId) ?
+ static_cast<sp<Camera3StreamInterface>>(mInputStream) :
+ static_cast<sp<Camera3StreamInterface>>(mOutputStreams.get(id));
+ // Verify number of outstanding buffers
+ if (stream->getOutstandingBuffersCount() != offlineStream.numOutstandingBuffers) {
+ SET_ERR("Offline stream %d # of remaining buffer mismatch: (%zu,%d) (service/HAL)",
+ id, stream->getOutstandingBuffersCount(), offlineStream.numOutstandingBuffers);
+ return UNKNOWN_ERROR;
}
}
- // ANDROID_STATISTICS_LENS_SHADING_MAP
- camera_metadata_ro_entry lsSizeEntry = deviceInfo.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
- camera_metadata_entry lsEntry = resultMetadata.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
- if (lsSizeEntry.count == 2 && lsEntry.count > 0
- && (int32_t)lsEntry.count == 4 * lsSizeEntry.data.i32[0] * lsSizeEntry.data.i32[1]) {
- for (int32_t i = 0; i < lsSizeEntry.data.i32[0] * lsSizeEntry.data.i32[1]; i++) {
- lsEntry.data.f[4*i+1] = lsEntry.data.f[4*i];
- lsEntry.data.f[4*i+2] = lsEntry.data.f[4*i];
- lsEntry.data.f[4*i+3] = lsEntry.data.f[4*i];
+ // Verify all streams to be deleted don't have any outstanding buffers
+ if (hasInputStream && std::find(offlineStreamIds.begin(), offlineStreamIds.end(),
+ inputStreamId) == offlineStreamIds.end()) {
+ if (mInputStream->hasOutstandingBuffers()) {
+ SET_ERR("Input stream %d still has %zu outstanding buffer!",
+ inputStreamId, mInputStream->getOutstandingBuffersCount());
+ return UNKNOWN_ERROR;
}
}
- // ANDROID_TONEMAP_CURVE_BLUE
- // ANDROID_TONEMAP_CURVE_GREEN
- // ANDROID_TONEMAP_CURVE_RED
- camera_metadata_entry tcbEntry = resultMetadata.find(ANDROID_TONEMAP_CURVE_BLUE);
- camera_metadata_entry tcgEntry = resultMetadata.find(ANDROID_TONEMAP_CURVE_GREEN);
- camera_metadata_entry tcrEntry = resultMetadata.find(ANDROID_TONEMAP_CURVE_RED);
- if (tcbEntry.count > 0
- && tcbEntry.count == tcgEntry.count
- && tcbEntry.count == tcrEntry.count) {
- for (size_t i = 0; i < tcbEntry.count; i++) {
- tcbEntry.data.f[i] = tcrEntry.data.f[i];
- tcgEntry.data.f[i] = tcrEntry.data.f[i];
+ for (const auto& outStreamId : outputStreamIds) {
+ if (std::find(offlineStreamIds.begin(), offlineStreamIds.end(),
+ outStreamId) == offlineStreamIds.end()) {
+ auto outStream = mOutputStreams.get(outStreamId);
+ if (outStream->hasOutstandingBuffers()) {
+ SET_ERR("Output stream %d still has %zu outstanding buffer!",
+ outStreamId, outStream->getOutstandingBuffersCount());
+ return UNKNOWN_ERROR;
+ }
}
}
- return res;
+ InFlightRequestMap offlineReqs;
+ // Verify inflight requests and their pending buffers
+ {
+ std::lock_guard<std::mutex> l(mInFlightLock);
+ for (auto offlineReq : offlineSessionInfo.offlineRequests) {
+ int idx = mInFlightMap.indexOfKey(offlineReq.frameNumber);
+ if (idx == NAME_NOT_FOUND) {
+ SET_ERR("Offline request frame number %d not found!", offlineReq.frameNumber);
+ return UNKNOWN_ERROR;
+ }
+
+ const auto& inflightReq = mInFlightMap.valueAt(idx);
+ // TODO: check specific stream IDs
+ size_t numBuffersLeft = static_cast<size_t>(inflightReq.numBuffersLeft);
+ if (numBuffersLeft != offlineReq.pendingStreams.size()) {
+ SET_ERR("Offline request # of remaining buffer mismatch: (%d,%d) (service/HAL)",
+ inflightReq.numBuffersLeft, offlineReq.pendingStreams.size());
+ return UNKNOWN_ERROR;
+ }
+ offlineReqs.add(offlineReq.frameNumber, inflightReq);
+ }
+ }
+
+ // Create Camera3OfflineSession and transfer object ownership
+ // (streams, inflight requests, buffer caches)
+ camera3::StreamSet offlineStreamSet;
+ sp<camera3::Camera3Stream> inputStream;
+ for (auto offlineStream : offlineSessionInfo.offlineStreams) {
+ int32_t id = offlineStream.id;
+ if (mInputStream != nullptr && id == mInputStream->getId()) {
+ inputStream = mInputStream;
+ } else {
+ offlineStreamSet.add(id, mOutputStreams.get(id));
+ }
+ }
+
+ // TODO: check if we need to lock before copying states
+ // though technically no other thread should be talking to Camera3Device at this point
+ Camera3OfflineStates offlineStates(
+ mTagMonitor, mVendorTagId, mUseHalBufManager, mNeedFixupMonochromeTags,
+ mUsePartialResult, mNumPartialResults, mNextResultFrameNumber,
+ mNextReprocessResultFrameNumber, mNextZslStillResultFrameNumber,
+ mNextShutterFrameNumber, mNextReprocessShutterFrameNumber,
+ mNextZslStillShutterFrameNumber, mDeviceInfo, mPhysicalDeviceInfoMap,
+ mDistortionMappers, mZoomRatioMappers, mRotateAndCropMappers);
+
+ *session = new Camera3OfflineSession(mId, inputStream, offlineStreamSet,
+ std::move(bufferRecords), offlineReqs, offlineStates, offlineSession);
+
+ // Delete all streams that has been transferred to offline session
+ Mutex::Autolock l(mLock);
+ for (auto offlineStream : offlineSessionInfo.offlineStreams) {
+ int32_t id = offlineStream.id;
+ if (mInputStream != nullptr && id == mInputStream->getId()) {
+ mInputStream.clear();
+ } else {
+ mOutputStreams.remove(id);
+ }
+ }
+
+ // disconnect all other streams and switch to UNCONFIGURED state
+ if (mInputStream != nullptr) {
+ ret = mInputStream->disconnect();
+ if (ret != OK) {
+ SET_ERR_L("disconnect input stream failed!");
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ for (auto streamId : mOutputStreams.getStreamIds()) {
+ sp<Camera3StreamInterface> stream = mOutputStreams.get(streamId);
+ ret = stream->disconnect();
+ if (ret != OK) {
+ SET_ERR_L("disconnect output stream %d failed!", streamId);
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ mInputStream.clear();
+ mOutputStreams.clear();
+ mNeedConfig = true;
+ internalUpdateStatusLocked(STATUS_UNCONFIGURED);
+ mOperatingMode = NO_MODE;
+ mIsConstrainedHighSpeedConfiguration = false;
+
+ return OK;
+ // TO be done by CameraDeviceClient/Camera3OfflineSession
+ // register the offline client to camera service
+ // Setup result passthing threads etc
+ // Initialize offline session so HAL can start sending callback to it (result Fmq)
+ // TODO: check how many onIdle callback will be sent
+ // Java side to make sure the CameraCaptureSession is properly closed
+}
+
+void Camera3Device::getOfflineStreamIds(std::vector<int> *offlineStreamIds) {
+ ATRACE_CALL();
+
+ if (offlineStreamIds == nullptr) {
+ return;
+ }
+
+ Mutex::Autolock il(mInterfaceLock);
+
+ auto streamIds = mOutputStreams.getStreamIds();
+ bool hasInputStream = mInputStream != nullptr;
+ if (hasInputStream && mInputStream->getOfflineProcessingSupport()) {
+ offlineStreamIds->push_back(mInputStream->getId());
+ }
+
+ for (const auto & streamId : streamIds) {
+ sp<camera3::Camera3OutputStreamInterface> stream = mOutputStreams.get(streamId);
+ // Streams that use the camera buffer manager are currently not supported in
+ // offline mode
+ if (stream->getOfflineProcessingSupport() &&
+ (stream->getStreamSetId() == CAMERA3_STREAM_SET_ID_INVALID)) {
+ offlineStreamIds->push_back(streamId);
+ }
+ }
+}
+
+status_t Camera3Device::setRotateAndCropAutoBehavior(
+ camera_metadata_enum_android_scaler_rotate_and_crop_t rotateAndCropValue) {
+ ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
+ Mutex::Autolock l(mLock);
+ if (mRequestThread == nullptr) {
+ return INVALID_OPERATION;
+ }
+ return mRequestThread->setRotateAndCropAutoBehavior(rotateAndCropValue);
}
}; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 2573b48..e13e45f 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -34,6 +34,7 @@
#include <android/hardware/camera/device/3.3/ICameraDeviceSession.h>
#include <android/hardware/camera/device/3.4/ICameraDeviceSession.h>
#include <android/hardware/camera/device/3.5/ICameraDeviceSession.h>
+#include <android/hardware/camera/device/3.6/ICameraDeviceSession.h>
#include <android/hardware/camera/device/3.2/ICameraDeviceCallback.h>
#include <android/hardware/camera/device/3.4/ICameraDeviceCallback.h>
#include <android/hardware/camera/device/3.5/ICameraDeviceCallback.h>
@@ -42,9 +43,15 @@
#include <camera/CaptureResult.h>
#include "common/CameraDeviceBase.h"
+#include "device3/BufferUtils.h"
#include "device3/StatusTracker.h"
#include "device3/Camera3BufferManager.h"
#include "device3/DistortionMapper.h"
+#include "device3/ZoomRatioMapper.h"
+#include "device3/RotateAndCropMapper.h"
+#include "device3/InFlightRequest.h"
+#include "device3/Camera3OutputInterface.h"
+#include "device3/Camera3OfflineSession.h"
#include "utils/TagMonitor.h"
#include "utils/LatencyHistogram.h"
#include <camera_metadata_hidden.h>
@@ -67,7 +74,11 @@
*/
class Camera3Device :
public CameraDeviceBase,
- virtual public hardware::camera::device::V3_5::ICameraDeviceCallback {
+ virtual public hardware::camera::device::V3_5::ICameraDeviceCallback,
+ public camera3::SetErrorInterface,
+ public camera3::InflightRequestUpdateInterface,
+ public camera3::RequestBufferInterface,
+ public camera3::FlushBufferInterface {
public:
explicit Camera3Device(const String8& id);
@@ -87,7 +98,7 @@
status_t disconnect() override;
status_t dump(int fd, const Vector<String16> &args) override;
const CameraMetadata& info() const override;
- const CameraMetadata& info(const String8& physicalId) const override;
+ const CameraMetadata& infoPhysical(const String8& physicalId) const override;
// Capture and setStreamingRequest will configure streams if currently in
// idle state
@@ -140,6 +151,8 @@
status_t getInputBufferProducer(
sp<IGraphicBufferProducer> *producer) override;
+ void getOfflineStreamIds(std::vector<int> *offlineStreamIds) override;
+
status_t createDefaultRequest(int templateId, CameraMetadata *request) override;
// Transitions to the idle state on success
@@ -196,6 +209,28 @@
nsecs_t getExpectedInFlightDuration() override;
+ status_t switchToOffline(const std::vector<int32_t>& streamsToKeep,
+ /*out*/ sp<CameraOfflineSessionBase>* session) override;
+
+ // RequestBufferInterface
+ bool startRequestBuffer() override;
+ void endRequestBuffer() override;
+ nsecs_t getWaitDuration() override;
+
+ // FlushBufferInterface
+ void getInflightBufferKeys(std::vector<std::pair<int32_t, int32_t>>* out) override;
+ void getInflightRequestBufferKeys(std::vector<uint64_t>* out) override;
+ std::vector<sp<camera3::Camera3StreamInterface>> getAllStreams() override;
+
+ /**
+ * Set the current behavior for the ROTATE_AND_CROP control when in AUTO.
+ *
+ * The value must be one of the ROTATE_AND_CROP_* values besides AUTO,
+ * and defaults to NONE.
+ */
+ status_t setRotateAndCropAutoBehavior(
+ camera_metadata_enum_android_scaler_rotate_and_crop_t rotateAndCropValue);
+
/**
* Helper functions to map between framework and HIDL values
*/
@@ -208,8 +243,6 @@
// Returns a negative error code if the passed-in operation mode is not valid.
static status_t mapToStreamConfigurationMode(camera3_stream_configuration_mode_t operationMode,
/*out*/ hardware::camera::device::V3_2::StreamConfigurationMode *mode);
- static camera3_buffer_status_t mapHidlBufferStatus(
- hardware::camera::device::V3_2::BufferStatus status);
static int mapToFrameworkFormat(hardware::graphics::common::V1_0::PixelFormat pixelFormat);
static android_dataspace mapToFrameworkDataspace(
hardware::camera::device::V3_2::DataspaceFlags);
@@ -224,7 +257,6 @@
// internal typedefs
using RequestMetadataQueue = hardware::MessageQueue<uint8_t, hardware::kSynchronizedReadWrite>;
- using ResultMetadataQueue = hardware::MessageQueue<uint8_t, hardware::kSynchronizedReadWrite>;
static const size_t kDumpLockAttempts = 10;
static const size_t kDumpSleepDuration = 100000; // 0.10 sec
@@ -278,11 +310,12 @@
* Adapter for legacy HAL / HIDL HAL interface calls; calls either into legacy HALv3 or the
* HIDL HALv3 interfaces.
*/
- class HalInterface : public camera3::Camera3StreamBufferFreedListener {
+ class HalInterface : public camera3::Camera3StreamBufferFreedListener,
+ public camera3::BufferRecordsInterface {
public:
HalInterface(sp<hardware::camera::device::V3_2::ICameraDeviceSession> &session,
std::shared_ptr<RequestMetadataQueue> queue,
- bool useHalBufManager);
+ bool useHalBufManager, bool supportOfflineProcessing);
HalInterface(const HalInterface &other);
HalInterface();
@@ -316,22 +349,31 @@
bool isReconfigurationRequired(CameraMetadata& oldSessionParams,
CameraMetadata& newSessionParams);
- // method to extract buffer's unique ID
- // return pair of (newlySeenBuffer?, bufferId)
- std::pair<bool, uint64_t> getBufferId(const buffer_handle_t& buf, int streamId);
+ // Upon successful return, HalInterface will return buffer maps needed for offline
+ // processing, and clear all its internal buffer maps.
+ status_t switchToOffline(
+ const std::vector<int32_t>& streamsToKeep,
+ /*out*/hardware::camera::device::V3_6::CameraOfflineSessionInfo* offlineSessionInfo,
+ /*out*/sp<hardware::camera::device::V3_6::ICameraOfflineSession>* offlineSession,
+ /*out*/camera3::BufferRecords* bufferRecords);
- // Find a buffer_handle_t based on frame number and stream ID
+ /////////////////////////////////////////////////////////////////////
+ // Implements BufferRecordsInterface
+
+ std::pair<bool, uint64_t> getBufferId(
+ const buffer_handle_t& buf, int streamId) override;
+
status_t popInflightBuffer(int32_t frameNumber, int32_t streamId,
- /*out*/ buffer_handle_t **buffer);
+ /*out*/ buffer_handle_t **buffer) override;
- // Register a bufId (streamId, buffer_handle_t) to inflight request buffer
status_t pushInflightRequestBuffer(
- uint64_t bufferId, buffer_handle_t* buf, int32_t streamId);
+ uint64_t bufferId, buffer_handle_t* buf, int32_t streamId) override;
- // Find a buffer_handle_t based on bufferId
status_t popInflightRequestBuffer(uint64_t bufferId,
/*out*/ buffer_handle_t** buffer,
- /*optional out*/ int32_t* streamId = nullptr);
+ /*optional out*/ int32_t* streamId = nullptr) override;
+
+ /////////////////////////////////////////////////////////////////////
// Get a vector of (frameNumber, streamId) pair of currently inflight
// buffers
@@ -342,7 +384,6 @@
void onStreamReConfigured(int streamId);
- static const uint64_t BUFFER_ID_NO_BUFFER = 0;
private:
// Always valid
sp<hardware::camera::device::V3_2::ICameraDeviceSession> mHidlSession;
@@ -352,11 +393,11 @@
sp<hardware::camera::device::V3_4::ICameraDeviceSession> mHidlSession_3_4;
// Valid if ICameraDeviceSession is @3.5 or newer
sp<hardware::camera::device::V3_5::ICameraDeviceSession> mHidlSession_3_5;
+ // Valid if ICameraDeviceSession is @3.6 or newer
+ sp<hardware::camera::device::V3_6::ICameraDeviceSession> mHidlSession_3_6;
std::shared_ptr<RequestMetadataQueue> mRequestMetadataQueue;
- std::mutex mInflightLock;
-
// The output HIDL request still depends on input camera3_capture_request_t
// Do not free input camera3_capture_request_t before output HIDL request
status_t wrapAsHidlRequest(camera3_capture_request_t* in,
@@ -370,65 +411,33 @@
// Pop inflight buffers based on pairs of (frameNumber,streamId)
void popInflightBuffers(const std::vector<std::pair<int32_t, int32_t>>& buffers);
- // Cache of buffer handles keyed off (frameNumber << 32 | streamId)
- std::unordered_map<uint64_t, buffer_handle_t*> mInflightBufferMap;
+ // Return true if the input caches match what we have; otherwise false
+ bool verifyBufferIds(int32_t streamId, std::vector<uint64_t>& inBufIds);
// Delete and optionally close native handles and clear the input vector afterward
static void cleanupNativeHandles(
std::vector<native_handle_t*> *handles, bool closeFd = false);
- struct BufferHasher {
- size_t operator()(const buffer_handle_t& buf) const {
- if (buf == nullptr)
- return 0;
-
- size_t result = 1;
- result = 31 * result + buf->numFds;
- for (int i = 0; i < buf->numFds; i++) {
- result = 31 * result + buf->data[i];
- }
- return result;
- }
- };
-
- struct BufferComparator {
- bool operator()(const buffer_handle_t& buf1, const buffer_handle_t& buf2) const {
- if (buf1->numFds == buf2->numFds) {
- for (int i = 0; i < buf1->numFds; i++) {
- if (buf1->data[i] != buf2->data[i]) {
- return false;
- }
- }
- return true;
- }
- return false;
- }
- };
-
- std::mutex mBufferIdMapLock; // protecting mBufferIdMaps and mNextBufferId
- typedef std::unordered_map<const buffer_handle_t, uint64_t,
- BufferHasher, BufferComparator> BufferIdMap;
- // stream ID -> per stream buffer ID map
- std::unordered_map<int, BufferIdMap> mBufferIdMaps;
- uint64_t mNextBufferId = 1; // 0 means no buffer
-
virtual void onBufferFreed(int streamId, const native_handle_t* handle) override;
+ std::mutex mFreedBuffersLock;
std::vector<std::pair<int, uint64_t>> mFreedBuffers;
- // Buffers given to HAL through requestStreamBuffer API
- std::mutex mRequestedBuffersLock;
- std::unordered_map<uint64_t, std::pair<int32_t, buffer_handle_t*>> mRequestedBuffers;
+ // Keep track of buffer cache and inflight buffer records
+ camera3::BufferRecords mBufferRecords;
uint32_t mNextStreamConfigCounter = 1;
const bool mUseHalBufManager;
bool mIsReconfigurationQuerySupported;
+
+ const bool mSupportOfflineProcessing;
};
sp<HalInterface> mInterface;
CameraMetadata mDeviceInfo;
+ bool mSupportNativeZoomRatio;
std::unordered_map<std::string, CameraMetadata> mPhysicalDeviceInfoMap;
CameraMetadata mRequestTemplateCache[CAMERA3_TEMPLATE_COUNT];
@@ -458,24 +467,7 @@
// Tracking cause of fatal errors when in STATUS_ERROR
String8 mErrorCause;
- // Synchronized mapping of stream IDs to stream instances
- class StreamSet {
- public:
- status_t add(int streamId, sp<camera3::Camera3OutputStreamInterface>);
- ssize_t remove(int streamId);
- sp<camera3::Camera3OutputStreamInterface> get(int streamId);
- // get by (underlying) vector index
- sp<camera3::Camera3OutputStreamInterface> operator[] (size_t index);
- size_t size() const;
- std::vector<int> getStreamIds();
- void clear();
-
- private:
- mutable std::mutex mLock;
- KeyedVector<int, sp<camera3::Camera3OutputStreamInterface>> mData;
- };
-
- StreamSet mOutputStreams;
+ camera3::StreamSet mOutputStreams;
sp<camera3::Camera3Stream> mInputStream;
int mNextStreamId;
bool mNeedConfig;
@@ -519,6 +511,10 @@
int mBatchSize;
// Whether this request is from a repeating or repeating burst.
bool mRepeating;
+ // Whether this request has ROTATE_AND_CROP_AUTO set, so needs both
+ // overriding of ROTATE_AND_CROP value and adjustment of coordinates
+ // in several other controls in both the request and the result
+ bool mRotateAndCropAuto;
};
typedef List<sp<CaptureRequest> > RequestList;
@@ -564,15 +560,6 @@
const hardware::hidl_vec<
hardware::camera::device::V3_2::StreamBuffer>& buffers) override;
- // Handle one capture result. Assume that mProcessCaptureResultLock is held.
- void processOneCaptureResultLocked(
- const hardware::camera::device::V3_2::CaptureResult& result,
- const hardware::hidl_vec<
- hardware::camera::device::V3_4::PhysicalCameraMetadata> physicalCameraMetadata);
- status_t readOneCameraMetadataLocked(uint64_t fmqResultSize,
- hardware::camera::device::V3_2::CameraMetadata& resultMetadata,
- const hardware::camera::device::V3_2::CameraMetadata& result);
-
// Handle one notify message
void notify(const hardware::camera::device::V3_2::NotifyMsg& msg);
@@ -695,11 +682,20 @@
* error message to indicate why. Only the first call's message will be
* used. The message is also sent to the log.
*/
- void setErrorState(const char *fmt, ...);
+ void setErrorState(const char *fmt, ...) override;
+ void setErrorStateLocked(const char *fmt, ...) override;
void setErrorStateV(const char *fmt, va_list args);
- void setErrorStateLocked(const char *fmt, ...);
void setErrorStateLockedV(const char *fmt, va_list args);
+ /////////////////////////////////////////////////////////////////////
+ // Implements InflightRequestUpdateInterface
+
+ void onInflightEntryRemovedLocked(nsecs_t duration) override;
+ void checkInflightMapLengthLocked() override;
+ void onInflightMapFlushedLocked() override;
+
+ /////////////////////////////////////////////////////////////////////
+
/**
* Debugging trylock/spin method
* Try to acquire a lock a few times with sleeps between before giving up.
@@ -707,12 +703,6 @@
bool tryLockSpinRightRound(Mutex& lock);
/**
- * Helper function to determine if an input size for implementation defined
- * format is supported.
- */
- bool isOpaqueInputSizeSupported(uint32_t width, uint32_t height);
-
- /**
* Helper function to get the largest Jpeg resolution (in area)
* Return Size(0, 0) if static metatdata is invalid
*/
@@ -842,6 +832,15 @@
void signalPipelineDrain(const std::vector<int>& streamIds);
+ status_t switchToOffline(
+ const std::vector<int32_t>& streamsToKeep,
+ /*out*/hardware::camera::device::V3_6::CameraOfflineSessionInfo* offlineSessionInfo,
+ /*out*/sp<hardware::camera::device::V3_6::ICameraOfflineSession>* offlineSession,
+ /*out*/camera3::BufferRecords* bufferRecords);
+
+ status_t setRotateAndCropAutoBehavior(
+ camera_metadata_enum_android_scaler_rotate_and_crop_t rotateAndCropValue);
+
protected:
virtual bool threadLoop();
@@ -858,10 +857,16 @@
// HAL workaround: Make sure a trigger ID always exists if
// a trigger does
- status_t addDummyTriggerIds(const sp<CaptureRequest> &request);
+ status_t addDummyTriggerIds(const sp<CaptureRequest> &request);
+
+ // Override rotate_and_crop control if needed; returns true if the current value was changed
+ bool overrideAutoRotateAndCrop(const sp<CaptureRequest> &request);
static const nsecs_t kRequestTimeout = 50e6; // 50 ms
+ // TODO: does this need to be adjusted for long exposure requests?
+ static const nsecs_t kRequestSubmitTimeout = 200e6; // 200 ms
+
// Used to prepare a batch of requests.
struct NextRequest {
sp<CaptureRequest> captureRequest;
@@ -936,6 +941,7 @@
Mutex mRequestLock;
Condition mRequestSignal;
+ Condition mRequestSubmittedSignal;
RequestList mRequestQueue;
RequestList mRepeatingRequests;
// The next batch of requests being prepped for submission to the HAL, no longer
@@ -958,6 +964,7 @@
sp<CaptureRequest> mPrevRequest;
int32_t mPrevTriggers;
+ std::set<std::string> mPrevCameraIdsWithZoom;
uint32_t mFrameNumber;
@@ -975,6 +982,7 @@
TriggerMap mTriggerReplacedMap;
uint32_t mCurrentAfTriggerId;
uint32_t mCurrentPreCaptureTriggerId;
+ camera_metadata_enum_android_scaler_rotate_and_crop_t mRotateAndCropOverride;
int64_t mRepeatingLastFrameNumber;
@@ -996,121 +1004,18 @@
/**
* In-flight queue for tracking completion of capture requests.
*/
+ std::mutex mInFlightLock;
+ camera3::InFlightRequestMap mInFlightMap;
+ nsecs_t mExpectedInflightDuration = 0;
+ // End of mInFlightLock protection scope
- struct InFlightRequest {
- // Set by notify() SHUTTER call.
- nsecs_t shutterTimestamp;
- // Set by process_capture_result().
- nsecs_t sensorTimestamp;
- int requestStatus;
- // Set by process_capture_result call with valid metadata
- bool haveResultMetadata;
- // Decremented by calls to process_capture_result with valid output
- // and input buffers
- int numBuffersLeft;
- CaptureResultExtras resultExtras;
- // If this request has any input buffer
- bool hasInputBuffer;
-
- // The last metadata that framework receives from HAL and
- // not yet send out because the shutter event hasn't arrived.
- // It's added by process_capture_result and sent when framework
- // receives the shutter event.
- CameraMetadata pendingMetadata;
-
- // The metadata of the partial results that framework receives from HAL so far
- // and has sent out.
- CameraMetadata collectedPartialResult;
-
- // Buffers are added by process_capture_result when output buffers
- // return from HAL but framework has not yet received the shutter
- // event. They will be returned to the streams when framework receives
- // the shutter event.
- Vector<camera3_stream_buffer_t> pendingOutputBuffers;
-
- // Whether this inflight request's shutter and result callback are to be
- // called. The policy is that if the request is the last one in the constrained
- // high speed recording request list, this flag will be true. If the request list
- // is not for constrained high speed recording, this flag will also be true.
- bool hasCallback;
-
- // Maximum expected frame duration for this request.
- // For manual captures, equal to the max of requested exposure time and frame duration
- // For auto-exposure modes, equal to 1/(lower end of target FPS range)
- nsecs_t maxExpectedDuration;
-
- // Whether the result metadata for this request is to be skipped. The
- // result metadata should be skipped in the case of
- // REQUEST/RESULT error.
- bool skipResultMetadata;
-
- // The physical camera ids being requested.
- std::set<String8> physicalCameraIds;
-
- // Map of physicalCameraId <-> Metadata
- std::vector<PhysicalCaptureResultInfo> physicalMetadatas;
-
- // Indicates a still capture request.
- bool stillCapture;
-
- // Indicates a ZSL capture request
- bool zslCapture;
-
- // What shared surfaces an output should go to
- SurfaceMap outputSurfaces;
-
- // Default constructor needed by KeyedVector
- InFlightRequest() :
- shutterTimestamp(0),
- sensorTimestamp(0),
- requestStatus(OK),
- haveResultMetadata(false),
- numBuffersLeft(0),
- hasInputBuffer(false),
- hasCallback(true),
- maxExpectedDuration(kDefaultExpectedDuration),
- skipResultMetadata(false),
- stillCapture(false),
- zslCapture(false) {
- }
-
- InFlightRequest(int numBuffers, CaptureResultExtras extras, bool hasInput,
- bool hasAppCallback, nsecs_t maxDuration,
- const std::set<String8>& physicalCameraIdSet, bool isStillCapture,
- bool isZslCapture,
- const SurfaceMap& outSurfaces = SurfaceMap{}) :
- shutterTimestamp(0),
- sensorTimestamp(0),
- requestStatus(OK),
- haveResultMetadata(false),
- numBuffersLeft(numBuffers),
- resultExtras(extras),
- hasInputBuffer(hasInput),
- hasCallback(hasAppCallback),
- maxExpectedDuration(maxDuration),
- skipResultMetadata(false),
- physicalCameraIds(physicalCameraIdSet),
- stillCapture(isStillCapture),
- zslCapture(isZslCapture),
- outputSurfaces(outSurfaces) {
- }
- };
-
- // Map from frame number to the in-flight request state
- typedef KeyedVector<uint32_t, InFlightRequest> InFlightMap;
-
-
- Mutex mInFlightLock; // Protects mInFlightMap and
- // mExpectedInflightDuration
- InFlightMap mInFlightMap;
- nsecs_t mExpectedInflightDuration = 0;
- int mInFlightStatusId;
+ int mInFlightStatusId; // const after initialize
status_t registerInFlight(uint32_t frameNumber,
int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput,
bool callback, nsecs_t maxExpectedDuration, std::set<String8>& physicalCameraIds,
- bool isStillCapture, bool isZslCapture,
- const SurfaceMap& outputSurfaces);
+ bool isStillCapture, bool isZslCapture, bool rotateAndCropAuto,
+ const std::set<std::string>& cameraIdsWithZoom, const SurfaceMap& outputSurfaces);
/**
* Tracking for idle detection
@@ -1182,7 +1087,7 @@
*/
// Lock for output side of device
- Mutex mOutputLock;
+ std::mutex mOutputLock;
/**** Scope for mOutputLock ****/
// the minimal frame number of the next non-reprocess result
@@ -1197,60 +1102,18 @@
uint32_t mNextReprocessShutterFrameNumber;
// the minimal frame number of the next ZSL still capture shutter
uint32_t mNextZslStillShutterFrameNumber;
- List<CaptureResult> mResultQueue;
- Condition mResultSignal;
- wp<NotificationListener> mListener;
+ List<CaptureResult> mResultQueue;
+ std::condition_variable mResultSignal;
+ wp<NotificationListener> mListener;
/**** End scope for mOutputLock ****/
- /**
- * Callback functions from HAL device
- */
- void processCaptureResult(const camera3_capture_result *result);
-
- void notify(const camera3_notify_msg *msg);
-
- // Specific notify handlers
- void notifyError(const camera3_error_msg_t &msg,
- sp<NotificationListener> listener);
- void notifyShutter(const camera3_shutter_msg_t &msg,
- sp<NotificationListener> listener);
-
- // helper function to return the output buffers to the streams.
- void returnOutputBuffers(const camera3_stream_buffer_t *outputBuffers,
- size_t numBuffers, nsecs_t timestamp, bool timestampIncreasing = true,
- // The following arguments are only meant for surface sharing use case
- const SurfaceMap& outputSurfaces = SurfaceMap{},
- // Used to send buffer error callback when failing to return buffer
- const CaptureResultExtras &resultExtras = CaptureResultExtras{});
-
- // Send a partial capture result.
- void sendPartialCaptureResult(const camera_metadata_t * partialResult,
- const CaptureResultExtras &resultExtras, uint32_t frameNumber);
-
- // Send a total capture result given the pending metadata and result extras,
- // partial results, and the frame number to the result queue.
- void sendCaptureResult(CameraMetadata &pendingMetadata,
- CaptureResultExtras &resultExtras,
- CameraMetadata &collectedPartialResult, uint32_t frameNumber,
- bool reprocess, bool zslStillCapture,
- const std::vector<PhysicalCaptureResultInfo>& physicalMetadatas);
-
- bool isLastFullResult(const InFlightRequest& inFlightRequest);
-
- // Insert the result to the result queue after updating frame number and overriding AE
- // trigger cancel.
- // mOutputLock must be held when calling this function.
- void insertResultLocked(CaptureResult *result, uint32_t frameNumber);
-
/**** Scope for mInFlightLock ****/
// Remove the in-flight map entry of the given index from mInFlightMap.
// It must only be called with mInFlightLock held.
void removeInFlightMapEntryLocked(int idx);
- // Remove the in-flight request of the given index from mInFlightMap
- // if it's no longer needed. It must only be called with mInFlightLock held.
- void removeInFlightRequestIfReadyLocked(int idx);
+
// Remove all in-flight requests and return all buffers.
// This is used after HAL interface is closed to cleanup any request/buffers
// not returned by HAL.
@@ -1266,6 +1129,16 @@
// logical camera and its physical subcameras.
std::unordered_map<std::string, camera3::DistortionMapper> mDistortionMappers;
+ /**
+ * Zoom ratio mapper support
+ */
+ std::unordered_map<std::string, camera3::ZoomRatioMapper> mZoomRatioMappers;
+
+ /**
+ * RotateAndCrop mapper support
+ */
+ std::unordered_map<std::string, camera3::RotateAndCropMapper> mRotateAndCropMappers;
+
// Debug tracker for metadata tag value changes
// - Enabled with the -m <taglist> option to dumpsys, such as
// dumpsys -m android.control.aeState,android.control.aeMode
@@ -1343,6 +1216,10 @@
void onSubmittingRequest();
void onRequestThreadPaused();
+ // Events triggered by successful switchToOffline call
+ // Return true is there is no ongoing requestBuffer call.
+ bool onSwitchToOfflineSuccess();
+
private:
void notifyTrackerLocked(bool active);
@@ -1356,6 +1233,7 @@
bool mRequestThreadPaused = true;
bool mInflightMapEmpty = true;
bool mRequestBufferOngoing = false;
+ bool mSwitchedToOffline = false;
wp<camera3::StatusTracker> mStatusTracker;
int mRequestBufferStatusId;
@@ -1363,7 +1241,9 @@
// Fix up result metadata for monochrome camera.
bool mNeedFixupMonochromeTags;
- status_t fixupMonochromeTags(const CameraMetadata& deviceInfo, CameraMetadata& resultMetadata);
+
+ // Whether HAL supports offline processing capability.
+ bool mSupportOfflineProcessing = false;
}; // class Camera3Device
}; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3OfflineSession.cpp b/services/camera/libcameraservice/device3/Camera3OfflineSession.cpp
new file mode 100644
index 0000000..5942868
--- /dev/null
+++ b/services/camera/libcameraservice/device3/Camera3OfflineSession.cpp
@@ -0,0 +1,470 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Camera3-OffLnSsn"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+//#define LOG_NNDEBUG 0 // Per-frame verbose logging
+
+#ifdef LOG_NNDEBUG
+#define ALOGVV(...) ALOGV(__VA_ARGS__)
+#else
+#define ALOGVV(...) ((void)0)
+#endif
+
+#include <inttypes.h>
+
+#include <utils/Trace.h>
+
+#include <android/hardware/camera2/ICameraDeviceCallbacks.h>
+
+#include "device3/Camera3OfflineSession.h"
+#include "device3/Camera3OutputStream.h"
+#include "device3/Camera3InputStream.h"
+#include "device3/Camera3SharedOutputStream.h"
+#include "utils/CameraTraces.h"
+
+using namespace android::camera3;
+using namespace android::hardware::camera;
+
+namespace android {
+
+Camera3OfflineSession::Camera3OfflineSession(const String8 &id,
+ const sp<camera3::Camera3Stream>& inputStream,
+ const camera3::StreamSet& offlineStreamSet,
+ camera3::BufferRecords&& bufferRecords,
+ const camera3::InFlightRequestMap& offlineReqs,
+ const Camera3OfflineStates& offlineStates,
+ sp<hardware::camera::device::V3_6::ICameraOfflineSession> offlineSession) :
+ mId(id),
+ mInputStream(inputStream),
+ mOutputStreams(offlineStreamSet),
+ mBufferRecords(std::move(bufferRecords)),
+ mOfflineReqs(offlineReqs),
+ mSession(offlineSession),
+ mTagMonitor(offlineStates.mTagMonitor),
+ mVendorTagId(offlineStates.mVendorTagId),
+ mUseHalBufManager(offlineStates.mUseHalBufManager),
+ mNeedFixupMonochromeTags(offlineStates.mNeedFixupMonochromeTags),
+ mUsePartialResult(offlineStates.mUsePartialResult),
+ mNumPartialResults(offlineStates.mNumPartialResults),
+ mNextResultFrameNumber(offlineStates.mNextResultFrameNumber),
+ mNextReprocessResultFrameNumber(offlineStates.mNextReprocessResultFrameNumber),
+ mNextZslStillResultFrameNumber(offlineStates.mNextZslStillResultFrameNumber),
+ mNextShutterFrameNumber(offlineStates.mNextShutterFrameNumber),
+ mNextReprocessShutterFrameNumber(offlineStates.mNextReprocessShutterFrameNumber),
+ mNextZslStillShutterFrameNumber(offlineStates.mNextZslStillShutterFrameNumber),
+ mDeviceInfo(offlineStates.mDeviceInfo),
+ mPhysicalDeviceInfoMap(offlineStates.mPhysicalDeviceInfoMap),
+ mDistortionMappers(offlineStates.mDistortionMappers),
+ mZoomRatioMappers(offlineStates.mZoomRatioMappers),
+ mRotateAndCropMappers(offlineStates.mRotateAndCropMappers),
+ mStatus(STATUS_UNINITIALIZED) {
+ ATRACE_CALL();
+ ALOGV("%s: Created offline session for camera %s", __FUNCTION__, mId.string());
+}
+
+Camera3OfflineSession::~Camera3OfflineSession() {
+ ATRACE_CALL();
+ ALOGV("%s: Tearing down offline session for camera id %s", __FUNCTION__, mId.string());
+ disconnectImpl();
+}
+
+const String8& Camera3OfflineSession::getId() const {
+ return mId;
+}
+
+status_t Camera3OfflineSession::initialize(wp<NotificationListener> listener) {
+ ATRACE_CALL();
+
+ if (mSession == nullptr) {
+ ALOGE("%s: HIDL session is null!", __FUNCTION__);
+ return DEAD_OBJECT;
+ }
+
+ {
+ std::lock_guard<std::mutex> lock(mLock);
+
+ mListener = listener;
+
+ // setup result FMQ
+ std::unique_ptr<ResultMetadataQueue>& resQueue = mResultMetadataQueue;
+ auto resultQueueRet = mSession->getCaptureResultMetadataQueue(
+ [&resQueue](const auto& descriptor) {
+ resQueue = std::make_unique<ResultMetadataQueue>(descriptor);
+ if (!resQueue->isValid() || resQueue->availableToWrite() <= 0) {
+ ALOGE("HAL returns empty result metadata fmq, not use it");
+ resQueue = nullptr;
+ // Don't use resQueue onwards.
+ }
+ });
+ if (!resultQueueRet.isOk()) {
+ ALOGE("Transaction error when getting result metadata queue from camera session: %s",
+ resultQueueRet.description().c_str());
+ return DEAD_OBJECT;
+ }
+ mStatus = STATUS_ACTIVE;
+ }
+
+ mSession->setCallback(this);
+
+ return OK;
+}
+
+status_t Camera3OfflineSession::dump(int /*fd*/) {
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> il(mInterfaceLock);
+ return OK;
+}
+
+status_t Camera3OfflineSession::disconnect() {
+ ATRACE_CALL();
+ return disconnectImpl();
+}
+
+status_t Camera3OfflineSession::disconnectImpl() {
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> il(mInterfaceLock);
+
+ sp<NotificationListener> listener;
+ {
+ std::lock_guard<std::mutex> lock(mLock);
+ if (mStatus == STATUS_CLOSED) {
+ return OK; // don't close twice
+ } else if (mStatus == STATUS_ERROR) {
+ ALOGE("%s: offline session %s shutting down in error state",
+ __FUNCTION__, mId.string());
+ }
+ listener = mListener.promote();
+ }
+
+ ALOGV("%s: E", __FUNCTION__);
+
+ {
+ std::lock_guard<std::mutex> lock(mRequestBufferInterfaceLock);
+ mAllowRequestBuffer = false;
+ }
+
+ std::vector<wp<Camera3StreamInterface>> streams;
+ streams.reserve(mOutputStreams.size() + (mInputStream != nullptr ? 1 : 0));
+ for (size_t i = 0; i < mOutputStreams.size(); i++) {
+ streams.push_back(mOutputStreams[i]);
+ }
+ if (mInputStream != nullptr) {
+ streams.push_back(mInputStream);
+ }
+
+ if (mSession != nullptr) {
+ mSession->close();
+ }
+
+ FlushInflightReqStates states {
+ mId, mOfflineReqsLock, mOfflineReqs, mUseHalBufManager,
+ listener, *this, mBufferRecords, *this};
+
+ camera3::flushInflightRequests(states);
+
+ {
+ std::lock_guard<std::mutex> lock(mLock);
+ mSession.clear();
+ mOutputStreams.clear();
+ mInputStream.clear();
+ mStatus = STATUS_CLOSED;
+ }
+
+ for (auto& weakStream : streams) {
+ sp<Camera3StreamInterface> stream = weakStream.promote();
+ if (stream != nullptr) {
+ ALOGE("%s: Stream %d leaked! strong reference (%d)!",
+ __FUNCTION__, stream->getId(), stream->getStrongCount() - 1);
+ }
+ }
+
+ ALOGV("%s: X", __FUNCTION__);
+ return OK;
+}
+
+status_t Camera3OfflineSession::waitForNextFrame(nsecs_t timeout) {
+ ATRACE_CALL();
+ std::unique_lock<std::mutex> lk(mOutputLock);
+
+ while (mResultQueue.empty()) {
+ auto st = mResultSignal.wait_for(lk, std::chrono::nanoseconds(timeout));
+ if (st == std::cv_status::timeout) {
+ return TIMED_OUT;
+ }
+ }
+ return OK;
+}
+
+status_t Camera3OfflineSession::getNextResult(CaptureResult* frame) {
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> l(mOutputLock);
+
+ if (mResultQueue.empty()) {
+ return NOT_ENOUGH_DATA;
+ }
+
+ if (frame == nullptr) {
+ ALOGE("%s: argument cannot be NULL", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ CaptureResult &result = *(mResultQueue.begin());
+ frame->mResultExtras = result.mResultExtras;
+ frame->mMetadata.acquire(result.mMetadata);
+ frame->mPhysicalMetadatas = std::move(result.mPhysicalMetadatas);
+ mResultQueue.erase(mResultQueue.begin());
+
+ return OK;
+}
+
+hardware::Return<void> Camera3OfflineSession::processCaptureResult_3_4(
+ const hardware::hidl_vec<
+ hardware::camera::device::V3_4::CaptureResult>& results) {
+ sp<NotificationListener> listener;
+ {
+ std::lock_guard<std::mutex> lock(mLock);
+ if (mStatus != STATUS_ACTIVE) {
+ ALOGE("%s called in wrong state %d", __FUNCTION__, mStatus);
+ return hardware::Void();
+ }
+ listener = mListener.promote();
+ }
+
+ CaptureOutputStates states {
+ mId,
+ mOfflineReqsLock, mOfflineReqs,
+ mOutputLock, mResultQueue, mResultSignal,
+ mNextShutterFrameNumber,
+ mNextReprocessShutterFrameNumber, mNextZslStillShutterFrameNumber,
+ mNextResultFrameNumber,
+ mNextReprocessResultFrameNumber, mNextZslStillResultFrameNumber,
+ mUseHalBufManager, mUsePartialResult, mNeedFixupMonochromeTags,
+ mNumPartialResults, mVendorTagId, mDeviceInfo, mPhysicalDeviceInfoMap,
+ mResultMetadataQueue, mDistortionMappers, mZoomRatioMappers, mRotateAndCropMappers,
+ mTagMonitor, mInputStream, mOutputStreams, listener, *this, *this, mBufferRecords
+ };
+
+ std::lock_guard<std::mutex> lock(mProcessCaptureResultLock);
+ for (const auto& result : results) {
+ processOneCaptureResultLocked(states, result.v3_2, result.physicalCameraMetadata);
+ }
+ return hardware::Void();
+}
+
+hardware::Return<void> Camera3OfflineSession::processCaptureResult(
+ const hardware::hidl_vec<
+ hardware::camera::device::V3_2::CaptureResult>& results) {
+ // TODO: changed impl to call into processCaptureResult_3_4 instead?
+ // might need to figure how to reduce copy though.
+ sp<NotificationListener> listener;
+ {
+ std::lock_guard<std::mutex> lock(mLock);
+ if (mStatus != STATUS_ACTIVE) {
+ ALOGE("%s called in wrong state %d", __FUNCTION__, mStatus);
+ return hardware::Void();
+ }
+ listener = mListener.promote();
+ }
+
+ hardware::hidl_vec<hardware::camera::device::V3_4::PhysicalCameraMetadata> noPhysMetadata;
+
+ CaptureOutputStates states {
+ mId,
+ mOfflineReqsLock, mOfflineReqs,
+ mOutputLock, mResultQueue, mResultSignal,
+ mNextShutterFrameNumber,
+ mNextReprocessShutterFrameNumber, mNextZslStillShutterFrameNumber,
+ mNextResultFrameNumber,
+ mNextReprocessResultFrameNumber, mNextZslStillResultFrameNumber,
+ mUseHalBufManager, mUsePartialResult, mNeedFixupMonochromeTags,
+ mNumPartialResults, mVendorTagId, mDeviceInfo, mPhysicalDeviceInfoMap,
+ mResultMetadataQueue, mDistortionMappers, mZoomRatioMappers, mRotateAndCropMappers,
+ mTagMonitor, mInputStream, mOutputStreams, listener, *this, *this, mBufferRecords
+ };
+
+ std::lock_guard<std::mutex> lock(mProcessCaptureResultLock);
+ for (const auto& result : results) {
+ processOneCaptureResultLocked(states, result, noPhysMetadata);
+ }
+ return hardware::Void();
+}
+
+hardware::Return<void> Camera3OfflineSession::notify(
+ const hardware::hidl_vec<hardware::camera::device::V3_2::NotifyMsg>& msgs) {
+ sp<NotificationListener> listener;
+ {
+ std::lock_guard<std::mutex> lock(mLock);
+ if (mStatus != STATUS_ACTIVE) {
+ ALOGE("%s called in wrong state %d", __FUNCTION__, mStatus);
+ return hardware::Void();
+ }
+ listener = mListener.promote();
+ }
+
+ CaptureOutputStates states {
+ mId,
+ mOfflineReqsLock, mOfflineReqs,
+ mOutputLock, mResultQueue, mResultSignal,
+ mNextShutterFrameNumber,
+ mNextReprocessShutterFrameNumber, mNextZslStillShutterFrameNumber,
+ mNextResultFrameNumber,
+ mNextReprocessResultFrameNumber, mNextZslStillResultFrameNumber,
+ mUseHalBufManager, mUsePartialResult, mNeedFixupMonochromeTags,
+ mNumPartialResults, mVendorTagId, mDeviceInfo, mPhysicalDeviceInfoMap,
+ mResultMetadataQueue, mDistortionMappers, mZoomRatioMappers, mRotateAndCropMappers,
+ mTagMonitor, mInputStream, mOutputStreams, listener, *this, *this, mBufferRecords
+ };
+ for (const auto& msg : msgs) {
+ camera3::notify(states, msg);
+ }
+ return hardware::Void();
+}
+
+hardware::Return<void> Camera3OfflineSession::requestStreamBuffers(
+ const hardware::hidl_vec<hardware::camera::device::V3_5::BufferRequest>& bufReqs,
+ requestStreamBuffers_cb _hidl_cb) {
+ {
+ std::lock_guard<std::mutex> lock(mLock);
+ if (mStatus != STATUS_ACTIVE) {
+ ALOGE("%s called in wrong state %d", __FUNCTION__, mStatus);
+ return hardware::Void();
+ }
+ }
+
+ RequestBufferStates states {
+ mId, mRequestBufferInterfaceLock, mUseHalBufManager, mOutputStreams,
+ *this, mBufferRecords, *this};
+ camera3::requestStreamBuffers(states, bufReqs, _hidl_cb);
+ return hardware::Void();
+}
+
+hardware::Return<void> Camera3OfflineSession::returnStreamBuffers(
+ const hardware::hidl_vec<hardware::camera::device::V3_2::StreamBuffer>& buffers) {
+ {
+ std::lock_guard<std::mutex> lock(mLock);
+ if (mStatus != STATUS_ACTIVE) {
+ ALOGE("%s called in wrong state %d", __FUNCTION__, mStatus);
+ return hardware::Void();
+ }
+ }
+
+ ReturnBufferStates states {
+ mId, mUseHalBufManager, mOutputStreams, mBufferRecords};
+ camera3::returnStreamBuffers(states, buffers);
+ return hardware::Void();
+}
+
+void Camera3OfflineSession::setErrorState(const char *fmt, ...) {
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> lock(mLock);
+ va_list args;
+ va_start(args, fmt);
+
+ setErrorStateLockedV(fmt, args);
+
+ va_end(args);
+
+ //FIXME: automatically disconnect here?
+}
+
+void Camera3OfflineSession::setErrorStateLocked(const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+
+ setErrorStateLockedV(fmt, args);
+
+ va_end(args);
+}
+
+void Camera3OfflineSession::setErrorStateLockedV(const char *fmt, va_list args) {
+ // Print out all error messages to log
+ String8 errorCause = String8::formatV(fmt, args);
+ ALOGE("Camera %s: %s", mId.string(), errorCause.string());
+
+ // But only do error state transition steps for the first error
+ if (mStatus == STATUS_ERROR || mStatus == STATUS_UNINITIALIZED) return;
+
+ mErrorCause = errorCause;
+
+ mStatus = STATUS_ERROR;
+
+ // Notify upstream about a device error
+ sp<NotificationListener> listener = mListener.promote();
+ if (listener != NULL) {
+ listener->notifyError(hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DEVICE,
+ CaptureResultExtras());
+ }
+
+ // Save stack trace. View by dumping it later.
+ CameraTraces::saveTrace();
+}
+
+void Camera3OfflineSession::onInflightEntryRemovedLocked(nsecs_t /*duration*/) {
+ if (mOfflineReqs.size() == 0) {
+ std::lock_guard<std::mutex> lock(mRequestBufferInterfaceLock);
+ mAllowRequestBuffer = false;
+ }
+}
+
+void Camera3OfflineSession::checkInflightMapLengthLocked() {
+ // Intentional empty impl.
+}
+
+void Camera3OfflineSession::onInflightMapFlushedLocked() {
+ // Intentional empty impl.
+}
+
+bool Camera3OfflineSession::startRequestBuffer() {
+ return mAllowRequestBuffer;
+}
+
+void Camera3OfflineSession::endRequestBuffer() {
+ // Intentional empty impl.
+}
+
+nsecs_t Camera3OfflineSession::getWaitDuration() {
+ const nsecs_t kBaseGetBufferWait = 3000000000; // 3 sec.
+ return kBaseGetBufferWait;
+}
+
+void Camera3OfflineSession::getInflightBufferKeys(std::vector<std::pair<int32_t, int32_t>>* out) {
+ mBufferRecords.getInflightBufferKeys(out);
+}
+
+void Camera3OfflineSession::getInflightRequestBufferKeys(std::vector<uint64_t>* out) {
+ mBufferRecords.getInflightRequestBufferKeys(out);
+}
+
+std::vector<sp<Camera3StreamInterface>> Camera3OfflineSession::getAllStreams() {
+ std::vector<sp<Camera3StreamInterface>> ret;
+ bool hasInputStream = mInputStream != nullptr;
+ ret.reserve(mOutputStreams.size() + ((hasInputStream) ? 1 : 0));
+ if (hasInputStream) {
+ ret.push_back(mInputStream);
+ }
+ for (size_t i = 0; i < mOutputStreams.size(); i++) {
+ ret.push_back(mOutputStreams[i]);
+ }
+ return ret;
+}
+
+const CameraMetadata& Camera3OfflineSession::info() const {
+ return mDeviceInfo;
+}
+
+}; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3OfflineSession.h b/services/camera/libcameraservice/device3/Camera3OfflineSession.h
new file mode 100644
index 0000000..27043d2
--- /dev/null
+++ b/services/camera/libcameraservice/device3/Camera3OfflineSession.h
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SERVERS_CAMERA3OFFLINESESSION_H
+#define ANDROID_SERVERS_CAMERA3OFFLINESESSION_H
+
+#include <memory>
+#include <mutex>
+
+#include <utils/String8.h>
+#include <utils/String16.h>
+
+#include <android/hardware/camera/device/3.6/ICameraOfflineSession.h>
+
+#include <fmq/MessageQueue.h>
+
+#include "common/CameraOfflineSessionBase.h"
+
+#include "device3/Camera3BufferManager.h"
+#include "device3/DistortionMapper.h"
+#include "device3/InFlightRequest.h"
+#include "device3/Camera3OutputUtils.h"
+#include "device3/RotateAndCropMapper.h"
+#include "device3/ZoomRatioMapper.h"
+#include "utils/TagMonitor.h"
+#include "utils/LatencyHistogram.h"
+#include <camera_metadata_hidden.h>
+
+namespace android {
+
+namespace camera3 {
+
+class Camera3Stream;
+class Camera3OutputStreamInterface;
+class Camera3StreamInterface;
+
+} // namespace camera3
+
+
+// An immutable struct containing general states that will be copied from Camera3Device to
+// Camera3OfflineSession
+struct Camera3OfflineStates {
+ Camera3OfflineStates(
+ const TagMonitor& tagMonitor, const metadata_vendor_id_t vendorTagId,
+ const bool useHalBufManager, const bool needFixupMonochromeTags,
+ const bool usePartialResult, const uint32_t numPartialResults,
+ const uint32_t nextResultFN, const uint32_t nextReprocResultFN,
+ const uint32_t nextZslResultFN, const uint32_t nextShutterFN,
+ const uint32_t nextReprocShutterFN, const uint32_t nextZslShutterFN,
+ const CameraMetadata& deviceInfo,
+ const std::unordered_map<std::string, CameraMetadata>& physicalDeviceInfoMap,
+ const std::unordered_map<std::string, camera3::DistortionMapper>& distortionMappers,
+ const std::unordered_map<std::string, camera3::ZoomRatioMapper>& zoomRatioMappers,
+ const std::unordered_map<std::string, camera3::RotateAndCropMapper>&
+ rotateAndCropMappers) :
+ mTagMonitor(tagMonitor), mVendorTagId(vendorTagId),
+ mUseHalBufManager(useHalBufManager), mNeedFixupMonochromeTags(needFixupMonochromeTags),
+ mUsePartialResult(usePartialResult), mNumPartialResults(numPartialResults),
+ mNextResultFrameNumber(nextResultFN),
+ mNextReprocessResultFrameNumber(nextReprocResultFN),
+ mNextZslStillResultFrameNumber(nextZslResultFN),
+ mNextShutterFrameNumber(nextShutterFN),
+ mNextReprocessShutterFrameNumber(nextReprocShutterFN),
+ mNextZslStillShutterFrameNumber(nextZslShutterFN),
+ mDeviceInfo(deviceInfo),
+ mPhysicalDeviceInfoMap(physicalDeviceInfoMap),
+ mDistortionMappers(distortionMappers),
+ mZoomRatioMappers(zoomRatioMappers),
+ mRotateAndCropMappers(rotateAndCropMappers) {}
+
+ const TagMonitor& mTagMonitor;
+ const metadata_vendor_id_t mVendorTagId;
+
+ const bool mUseHalBufManager;
+ const bool mNeedFixupMonochromeTags;
+
+ const bool mUsePartialResult;
+ const uint32_t mNumPartialResults;
+
+ // the minimal frame number of the next non-reprocess result
+ const uint32_t mNextResultFrameNumber;
+ // the minimal frame number of the next reprocess result
+ const uint32_t mNextReprocessResultFrameNumber;
+ // the minimal frame number of the next ZSL still capture result
+ const uint32_t mNextZslStillResultFrameNumber;
+ // the minimal frame number of the next non-reprocess shutter
+ const uint32_t mNextShutterFrameNumber;
+ // the minimal frame number of the next reprocess shutter
+ const uint32_t mNextReprocessShutterFrameNumber;
+ // the minimal frame number of the next ZSL still capture shutter
+ const uint32_t mNextZslStillShutterFrameNumber;
+
+ const CameraMetadata& mDeviceInfo;
+
+ const std::unordered_map<std::string, CameraMetadata>& mPhysicalDeviceInfoMap;
+
+ const std::unordered_map<std::string, camera3::DistortionMapper>& mDistortionMappers;
+
+ const std::unordered_map<std::string, camera3::ZoomRatioMapper>& mZoomRatioMappers;
+
+ const std::unordered_map<std::string, camera3::RotateAndCropMapper>& mRotateAndCropMappers;
+};
+
+/**
+ * Camera3OfflineSession for offline session defined in HIDL ICameraOfflineSession@3.6 or higher
+ */
+class Camera3OfflineSession :
+ public CameraOfflineSessionBase,
+ virtual public hardware::camera::device::V3_5::ICameraDeviceCallback,
+ public camera3::SetErrorInterface,
+ public camera3::InflightRequestUpdateInterface,
+ public camera3::RequestBufferInterface,
+ public camera3::FlushBufferInterface {
+ public:
+
+ // initialize by Camera3Device.
+ explicit Camera3OfflineSession(const String8& id,
+ const sp<camera3::Camera3Stream>& inputStream,
+ const camera3::StreamSet& offlineStreamSet,
+ camera3::BufferRecords&& bufferRecords,
+ const camera3::InFlightRequestMap& offlineReqs,
+ const Camera3OfflineStates& offlineStates,
+ sp<hardware::camera::device::V3_6::ICameraOfflineSession> offlineSession);
+
+ virtual ~Camera3OfflineSession();
+
+ virtual status_t initialize(wp<NotificationListener> listener) override;
+
+ /**
+ * CameraOfflineSessionBase interface
+ */
+ status_t disconnect() override;
+ status_t dump(int fd) override;
+
+ /**
+ * FrameProducer interface
+ */
+ const String8& getId() const override;
+ const CameraMetadata& info() const override;
+ status_t waitForNextFrame(nsecs_t timeout) override;
+ status_t getNextResult(CaptureResult *frame) override;
+
+ // TODO: methods for notification (error/idle/finished etc) passing
+
+ /**
+ * End of CameraOfflineSessionBase interface
+ */
+
+ /**
+ * HIDL ICameraDeviceCallback interface
+ */
+
+ /**
+ * Implementation of android::hardware::camera::device::V3_5::ICameraDeviceCallback
+ */
+
+ hardware::Return<void> processCaptureResult_3_4(
+ const hardware::hidl_vec<
+ hardware::camera::device::V3_4::CaptureResult>& results) override;
+ hardware::Return<void> processCaptureResult(
+ const hardware::hidl_vec<
+ hardware::camera::device::V3_2::CaptureResult>& results) override;
+ hardware::Return<void> notify(
+ const hardware::hidl_vec<
+ hardware::camera::device::V3_2::NotifyMsg>& msgs) override;
+
+ hardware::Return<void> requestStreamBuffers(
+ const hardware::hidl_vec<
+ hardware::camera::device::V3_5::BufferRequest>& bufReqs,
+ requestStreamBuffers_cb _hidl_cb) override;
+
+ hardware::Return<void> returnStreamBuffers(
+ const hardware::hidl_vec<
+ hardware::camera::device::V3_2::StreamBuffer>& buffers) override;
+
+ /**
+ * End of CameraOfflineSessionBase interface
+ */
+
+ private:
+ // Camera device ID
+ const String8 mId;
+ sp<camera3::Camera3Stream> mInputStream;
+ camera3::StreamSet mOutputStreams;
+ camera3::BufferRecords mBufferRecords;
+
+ std::mutex mOfflineReqsLock;
+ camera3::InFlightRequestMap mOfflineReqs;
+
+ sp<hardware::camera::device::V3_6::ICameraOfflineSession> mSession;
+
+ TagMonitor mTagMonitor;
+ const metadata_vendor_id_t mVendorTagId;
+
+ const bool mUseHalBufManager;
+ const bool mNeedFixupMonochromeTags;
+
+ const bool mUsePartialResult;
+ const uint32_t mNumPartialResults;
+
+ std::mutex mOutputLock;
+ List<CaptureResult> mResultQueue;
+ std::condition_variable mResultSignal;
+ // the minimal frame number of the next non-reprocess result
+ uint32_t mNextResultFrameNumber;
+ // the minimal frame number of the next reprocess result
+ uint32_t mNextReprocessResultFrameNumber;
+ // the minimal frame number of the next ZSL still capture result
+ uint32_t mNextZslStillResultFrameNumber;
+ // the minimal frame number of the next non-reprocess shutter
+ uint32_t mNextShutterFrameNumber;
+ // the minimal frame number of the next reprocess shutter
+ uint32_t mNextReprocessShutterFrameNumber;
+ // the minimal frame number of the next ZSL still capture shutter
+ uint32_t mNextZslStillShutterFrameNumber;
+ // End of mOutputLock scope
+
+ const CameraMetadata mDeviceInfo;
+ std::unordered_map<std::string, CameraMetadata> mPhysicalDeviceInfoMap;
+
+ std::unordered_map<std::string, camera3::DistortionMapper> mDistortionMappers;
+
+ std::unordered_map<std::string, camera3::ZoomRatioMapper> mZoomRatioMappers;
+
+ std::unordered_map<std::string, camera3::RotateAndCropMapper> mRotateAndCropMappers;
+
+ mutable std::mutex mLock;
+
+ enum Status {
+ STATUS_UNINITIALIZED = 0,
+ STATUS_ACTIVE,
+ STATUS_ERROR,
+ STATUS_CLOSED
+ } mStatus;
+
+ wp<NotificationListener> mListener;
+ // End of mLock protect scope
+
+ std::mutex mProcessCaptureResultLock;
+ // FMQ to write result on. Must be guarded by mProcessCaptureResultLock.
+ std::unique_ptr<ResultMetadataQueue> mResultMetadataQueue;
+
+ // Tracking cause of fatal errors when in STATUS_ERROR
+ String8 mErrorCause;
+
+ // Lock to ensure requestStreamBuffers() callbacks are serialized
+ std::mutex mRequestBufferInterfaceLock;
+ // allow request buffer until all requests are processed or disconnectImpl is called
+ bool mAllowRequestBuffer = true;
+
+ // For client methods such as disconnect/dump
+ std::mutex mInterfaceLock;
+
+ // SetErrorInterface
+ void setErrorState(const char *fmt, ...) override;
+ void setErrorStateLocked(const char *fmt, ...) override;
+
+ // InflightRequestUpdateInterface
+ void onInflightEntryRemovedLocked(nsecs_t duration) override;
+ void checkInflightMapLengthLocked() override;
+ void onInflightMapFlushedLocked() override;
+
+ // RequestBufferInterface
+ bool startRequestBuffer() override;
+ void endRequestBuffer() override;
+ nsecs_t getWaitDuration() override;
+
+ // FlushBufferInterface
+ void getInflightBufferKeys(std::vector<std::pair<int32_t, int32_t>>* out) override;
+ void getInflightRequestBufferKeys(std::vector<uint64_t>* out) override;
+ std::vector<sp<camera3::Camera3StreamInterface>> getAllStreams() override;
+
+ void setErrorStateLockedV(const char *fmt, va_list args);
+
+ status_t disconnectImpl();
+}; // class Camera3OfflineSession
+
+}; // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/device3/Camera3OutputInterface.h b/services/camera/libcameraservice/device3/Camera3OutputInterface.h
new file mode 100644
index 0000000..8817833
--- /dev/null
+++ b/services/camera/libcameraservice/device3/Camera3OutputInterface.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SERVERS_CAMERA3_OUTPUT_INTERFACE_H
+#define ANDROID_SERVERS_CAMERA3_OUTPUT_INTERFACE_H
+
+#include <memory>
+
+#include <cutils/native_handle.h>
+
+#include <utils/Timers.h>
+
+#include "device3/Camera3StreamInterface.h"
+
+namespace android {
+
+namespace camera3 {
+
+ /**
+ * Interfaces used by result/notification path shared between Camera3Device and
+ * Camera3OfflineSession
+ */
+ class SetErrorInterface {
+ public:
+ // Switch device into error state and send a ERROR_DEVICE notification
+ virtual void setErrorState(const char *fmt, ...) = 0;
+ // Same as setErrorState except this method assumes callers holds the main object lock
+ virtual void setErrorStateLocked(const char *fmt, ...) = 0;
+
+ virtual ~SetErrorInterface() {}
+ };
+
+ // Interface used by callback path to update buffer records
+ class BufferRecordsInterface {
+ public:
+ // method to extract buffer's unique ID
+ // return pair of (newlySeenBuffer?, bufferId)
+ virtual std::pair<bool, uint64_t> getBufferId(const buffer_handle_t& buf, int streamId) = 0;
+
+ // Find a buffer_handle_t based on frame number and stream ID
+ virtual status_t popInflightBuffer(int32_t frameNumber, int32_t streamId,
+ /*out*/ buffer_handle_t **buffer) = 0;
+
+ // Register a bufId (streamId, buffer_handle_t) to inflight request buffer
+ virtual status_t pushInflightRequestBuffer(
+ uint64_t bufferId, buffer_handle_t* buf, int32_t streamId) = 0;
+
+ // Find a buffer_handle_t based on bufferId
+ virtual status_t popInflightRequestBuffer(uint64_t bufferId,
+ /*out*/ buffer_handle_t** buffer,
+ /*optional out*/ int32_t* streamId = nullptr) = 0;
+
+ virtual ~BufferRecordsInterface() {}
+ };
+
+ class InflightRequestUpdateInterface {
+ public:
+ // Caller must hold the lock proctecting InflightRequestMap
+ // duration: the maxExpectedDuration of the removed entry
+ virtual void onInflightEntryRemovedLocked(nsecs_t duration) = 0;
+
+ virtual void checkInflightMapLengthLocked() = 0;
+
+ virtual void onInflightMapFlushedLocked() = 0;
+
+ virtual ~InflightRequestUpdateInterface() {}
+ };
+
+ class RequestBufferInterface {
+ public:
+ // Return if the state machine currently allows for requestBuffers.
+ // If this returns true, caller must call endRequestBuffer() later to signal end of a
+ // request buffer transaction.
+ virtual bool startRequestBuffer() = 0;
+
+ virtual void endRequestBuffer() = 0;
+
+ // Returns how long should implementation wait for a buffer returned
+ virtual nsecs_t getWaitDuration() = 0;
+
+ virtual ~RequestBufferInterface() {}
+ };
+
+ class FlushBufferInterface {
+ public:
+ virtual void getInflightBufferKeys(std::vector<std::pair<int32_t, int32_t>>* out) = 0;
+
+ virtual void getInflightRequestBufferKeys(std::vector<uint64_t>* out) = 0;
+
+ virtual std::vector<sp<Camera3StreamInterface>> getAllStreams() = 0;
+
+ virtual ~FlushBufferInterface() {}
+ };
+} // namespace camera3
+
+} // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.cpp b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.cpp
new file mode 100644
index 0000000..64af91e
--- /dev/null
+++ b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Camera3-OutStrmIntf"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+//#define LOG_NNDEBUG 0 // Per-frame verbose logging
+
+
+#include "Camera3OutputStreamInterface.h"
+
+namespace android {
+
+namespace camera3 {
+
+status_t StreamSet::add(
+ int streamId, sp<camera3::Camera3OutputStreamInterface> stream) {
+ if (stream == nullptr) {
+ ALOGE("%s: cannot add null stream", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ std::lock_guard<std::mutex> lock(mLock);
+ return mData.add(streamId, stream);
+}
+
+ssize_t StreamSet::remove(int streamId) {
+ std::lock_guard<std::mutex> lock(mLock);
+ return mData.removeItem(streamId);
+}
+
+sp<camera3::Camera3OutputStreamInterface> StreamSet::get(int streamId) {
+ std::lock_guard<std::mutex> lock(mLock);
+ ssize_t idx = mData.indexOfKey(streamId);
+ if (idx == NAME_NOT_FOUND) {
+ return nullptr;
+ }
+ return mData.editValueAt(idx);
+}
+
+sp<camera3::Camera3OutputStreamInterface> StreamSet::operator[] (size_t index) {
+ std::lock_guard<std::mutex> lock(mLock);
+ return mData.editValueAt(index);
+}
+
+size_t StreamSet::size() const {
+ std::lock_guard<std::mutex> lock(mLock);
+ return mData.size();
+}
+
+void StreamSet::clear() {
+ std::lock_guard<std::mutex> lock(mLock);
+ return mData.clear();
+}
+
+std::vector<int> StreamSet::getStreamIds() {
+ std::lock_guard<std::mutex> lock(mLock);
+ std::vector<int> streamIds(mData.size());
+ for (size_t i = 0; i < mData.size(); i++) {
+ streamIds[i] = mData.keyAt(i);
+ }
+ return streamIds;
+}
+
+StreamSet::StreamSet(const StreamSet& other) {
+ std::lock_guard<std::mutex> lock(other.mLock);
+ mData = other.mData;
+}
+
+} // namespace camera3
+
+} // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
index 2bde949..7f5c87a 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
@@ -97,6 +97,26 @@
virtual const String8& getPhysicalCameraId() const = 0;
};
+// Helper class to organize a synchronized mapping of stream IDs to stream instances
+class StreamSet {
+ public:
+ status_t add(int streamId, sp<camera3::Camera3OutputStreamInterface>);
+ ssize_t remove(int streamId);
+ sp<camera3::Camera3OutputStreamInterface> get(int streamId);
+ // get by (underlying) vector index
+ sp<camera3::Camera3OutputStreamInterface> operator[] (size_t index);
+ size_t size() const;
+ std::vector<int> getStreamIds();
+ void clear();
+
+ StreamSet() {};
+ StreamSet(const StreamSet& other);
+
+ private:
+ mutable std::mutex mLock;
+ KeyedVector<int, sp<camera3::Camera3OutputStreamInterface>> mData;
+};
+
} // namespace camera3
} // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp b/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
new file mode 100644
index 0000000..39b7db4
--- /dev/null
+++ b/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
@@ -0,0 +1,1430 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Camera3-OutputUtils"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+//#define LOG_NNDEBUG 0 // Per-frame verbose logging
+
+#ifdef LOG_NNDEBUG
+#define ALOGVV(...) ALOGV(__VA_ARGS__)
+#else
+#define ALOGVV(...) ((void)0)
+#endif
+
+// Convenience macros for transitioning to the error state
+#define SET_ERR(fmt, ...) states.setErrIntf.setErrorState( \
+ "%s: " fmt, __FUNCTION__, \
+ ##__VA_ARGS__)
+
+#include <inttypes.h>
+
+#include <utils/Log.h>
+#include <utils/SortedVector.h>
+#include <utils/Trace.h>
+
+#include <android/hardware/camera2/ICameraDeviceCallbacks.h>
+
+#include <android/hardware/camera/device/3.4/ICameraDeviceCallback.h>
+#include <android/hardware/camera/device/3.5/ICameraDeviceCallback.h>
+#include <android/hardware/camera/device/3.5/ICameraDeviceSession.h>
+
+#include <camera_metadata_hidden.h>
+
+#include "device3/Camera3OutputUtils.h"
+
+using namespace android::camera3;
+using namespace android::hardware::camera;
+
+namespace android {
+namespace camera3 {
+
+status_t fixupMonochromeTags(
+ CaptureOutputStates& states,
+ const CameraMetadata& deviceInfo,
+ CameraMetadata& resultMetadata) {
+ status_t res = OK;
+ if (!states.needFixupMonoChrome) {
+ return res;
+ }
+
+ // Remove tags that are not applicable to monochrome camera.
+ int32_t tagsToRemove[] = {
+ ANDROID_SENSOR_GREEN_SPLIT,
+ ANDROID_SENSOR_NEUTRAL_COLOR_POINT,
+ ANDROID_COLOR_CORRECTION_MODE,
+ ANDROID_COLOR_CORRECTION_TRANSFORM,
+ ANDROID_COLOR_CORRECTION_GAINS,
+ };
+ for (auto tag : tagsToRemove) {
+ res = resultMetadata.erase(tag);
+ if (res != OK) {
+ ALOGE("%s: Failed to remove tag %d for monochrome camera", __FUNCTION__, tag);
+ return res;
+ }
+ }
+
+ // ANDROID_SENSOR_DYNAMIC_BLACK_LEVEL
+ camera_metadata_entry blEntry = resultMetadata.find(ANDROID_SENSOR_DYNAMIC_BLACK_LEVEL);
+ for (size_t i = 1; i < blEntry.count; i++) {
+ blEntry.data.f[i] = blEntry.data.f[0];
+ }
+
+ // ANDROID_SENSOR_NOISE_PROFILE
+ camera_metadata_entry npEntry = resultMetadata.find(ANDROID_SENSOR_NOISE_PROFILE);
+ if (npEntry.count > 0 && npEntry.count % 2 == 0) {
+ double np[] = {npEntry.data.d[0], npEntry.data.d[1]};
+ res = resultMetadata.update(ANDROID_SENSOR_NOISE_PROFILE, np, 2);
+ if (res != OK) {
+ ALOGE("%s: Failed to update SENSOR_NOISE_PROFILE: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+ }
+
+ // ANDROID_STATISTICS_LENS_SHADING_MAP
+ camera_metadata_ro_entry lsSizeEntry = deviceInfo.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
+ camera_metadata_entry lsEntry = resultMetadata.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
+ if (lsSizeEntry.count == 2 && lsEntry.count > 0
+ && (int32_t)lsEntry.count == 4 * lsSizeEntry.data.i32[0] * lsSizeEntry.data.i32[1]) {
+ for (int32_t i = 0; i < lsSizeEntry.data.i32[0] * lsSizeEntry.data.i32[1]; i++) {
+ lsEntry.data.f[4*i+1] = lsEntry.data.f[4*i];
+ lsEntry.data.f[4*i+2] = lsEntry.data.f[4*i];
+ lsEntry.data.f[4*i+3] = lsEntry.data.f[4*i];
+ }
+ }
+
+ // ANDROID_TONEMAP_CURVE_BLUE
+ // ANDROID_TONEMAP_CURVE_GREEN
+ // ANDROID_TONEMAP_CURVE_RED
+ camera_metadata_entry tcbEntry = resultMetadata.find(ANDROID_TONEMAP_CURVE_BLUE);
+ camera_metadata_entry tcgEntry = resultMetadata.find(ANDROID_TONEMAP_CURVE_GREEN);
+ camera_metadata_entry tcrEntry = resultMetadata.find(ANDROID_TONEMAP_CURVE_RED);
+ if (tcbEntry.count > 0
+ && tcbEntry.count == tcgEntry.count
+ && tcbEntry.count == tcrEntry.count) {
+ for (size_t i = 0; i < tcbEntry.count; i++) {
+ tcbEntry.data.f[i] = tcrEntry.data.f[i];
+ tcgEntry.data.f[i] = tcrEntry.data.f[i];
+ }
+ }
+
+ return res;
+}
+
+void insertResultLocked(CaptureOutputStates& states, CaptureResult *result, uint32_t frameNumber) {
+ if (result == nullptr) return;
+
+ camera_metadata_t *meta = const_cast<camera_metadata_t *>(
+ result->mMetadata.getAndLock());
+ set_camera_metadata_vendor_id(meta, states.vendorTagId);
+ result->mMetadata.unlock(meta);
+
+ if (result->mMetadata.update(ANDROID_REQUEST_FRAME_COUNT,
+ (int32_t*)&frameNumber, 1) != OK) {
+ SET_ERR("Failed to set frame number %d in metadata", frameNumber);
+ return;
+ }
+
+ if (result->mMetadata.update(ANDROID_REQUEST_ID, &result->mResultExtras.requestId, 1) != OK) {
+ SET_ERR("Failed to set request ID in metadata for frame %d", frameNumber);
+ return;
+ }
+
+ // Update vendor tag id for physical metadata
+ for (auto& physicalMetadata : result->mPhysicalMetadatas) {
+ camera_metadata_t *pmeta = const_cast<camera_metadata_t *>(
+ physicalMetadata.mPhysicalCameraMetadata.getAndLock());
+ set_camera_metadata_vendor_id(pmeta, states.vendorTagId);
+ physicalMetadata.mPhysicalCameraMetadata.unlock(pmeta);
+ }
+
+ // Valid result, insert into queue
+ List<CaptureResult>::iterator queuedResult =
+ states.resultQueue.insert(states.resultQueue.end(), CaptureResult(*result));
+ ALOGVV("%s: result requestId = %" PRId32 ", frameNumber = %" PRId64
+ ", burstId = %" PRId32, __FUNCTION__,
+ queuedResult->mResultExtras.requestId,
+ queuedResult->mResultExtras.frameNumber,
+ queuedResult->mResultExtras.burstId);
+
+ states.resultSignal.notify_one();
+}
+
+
+void sendPartialCaptureResult(CaptureOutputStates& states,
+ const camera_metadata_t * partialResult,
+ const CaptureResultExtras &resultExtras, uint32_t frameNumber) {
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> l(states.outputLock);
+
+ CaptureResult captureResult;
+ captureResult.mResultExtras = resultExtras;
+ captureResult.mMetadata = partialResult;
+
+ // Fix up result metadata for monochrome camera.
+ status_t res = fixupMonochromeTags(states, states.deviceInfo, captureResult.mMetadata);
+ if (res != OK) {
+ SET_ERR("Failed to override result metadata: %s (%d)", strerror(-res), res);
+ return;
+ }
+
+ insertResultLocked(states, &captureResult, frameNumber);
+}
+
+void sendCaptureResult(
+ CaptureOutputStates& states,
+ CameraMetadata &pendingMetadata,
+ CaptureResultExtras &resultExtras,
+ CameraMetadata &collectedPartialResult,
+ uint32_t frameNumber,
+ bool reprocess, bool zslStillCapture, bool rotateAndCropAuto,
+ const std::set<std::string>& cameraIdsWithZoom,
+ const std::vector<PhysicalCaptureResultInfo>& physicalMetadatas) {
+ ATRACE_CALL();
+ if (pendingMetadata.isEmpty())
+ return;
+
+ std::lock_guard<std::mutex> l(states.outputLock);
+
+ // TODO: need to track errors for tighter bounds on expected frame number
+ if (reprocess) {
+ if (frameNumber < states.nextReprocResultFrameNum) {
+ SET_ERR("Out-of-order reprocess capture result metadata submitted! "
+ "(got frame number %d, expecting %d)",
+ frameNumber, states.nextReprocResultFrameNum);
+ return;
+ }
+ states.nextReprocResultFrameNum = frameNumber + 1;
+ } else if (zslStillCapture) {
+ if (frameNumber < states.nextZslResultFrameNum) {
+ SET_ERR("Out-of-order ZSL still capture result metadata submitted! "
+ "(got frame number %d, expecting %d)",
+ frameNumber, states.nextZslResultFrameNum);
+ return;
+ }
+ states.nextZslResultFrameNum = frameNumber + 1;
+ } else {
+ if (frameNumber < states.nextResultFrameNum) {
+ SET_ERR("Out-of-order capture result metadata submitted! "
+ "(got frame number %d, expecting %d)",
+ frameNumber, states.nextResultFrameNum);
+ return;
+ }
+ states.nextResultFrameNum = frameNumber + 1;
+ }
+
+ CaptureResult captureResult;
+ captureResult.mResultExtras = resultExtras;
+ captureResult.mMetadata = pendingMetadata;
+ captureResult.mPhysicalMetadatas = physicalMetadatas;
+
+ // Append any previous partials to form a complete result
+ if (states.usePartialResult && !collectedPartialResult.isEmpty()) {
+ captureResult.mMetadata.append(collectedPartialResult);
+ }
+
+ captureResult.mMetadata.sort();
+
+ // Check that there's a timestamp in the result metadata
+ camera_metadata_entry timestamp = captureResult.mMetadata.find(ANDROID_SENSOR_TIMESTAMP);
+ if (timestamp.count == 0) {
+ SET_ERR("No timestamp provided by HAL for frame %d!",
+ frameNumber);
+ return;
+ }
+ for (auto& physicalMetadata : captureResult.mPhysicalMetadatas) {
+ camera_metadata_entry timestamp =
+ physicalMetadata.mPhysicalCameraMetadata.find(ANDROID_SENSOR_TIMESTAMP);
+ if (timestamp.count == 0) {
+ SET_ERR("No timestamp provided by HAL for physical camera %s frame %d!",
+ String8(physicalMetadata.mPhysicalCameraId).c_str(), frameNumber);
+ return;
+ }
+ }
+
+ // Fix up some result metadata to account for HAL-level distortion correction
+ status_t res =
+ states.distortionMappers[states.cameraId.c_str()].correctCaptureResult(
+ &captureResult.mMetadata);
+ if (res != OK) {
+ SET_ERR("Unable to correct capture result metadata for frame %d: %s (%d)",
+ frameNumber, strerror(-res), res);
+ return;
+ }
+
+ // Fix up result metadata to account for zoom ratio availabilities between
+ // HAL and app.
+ bool zoomRatioIs1 = cameraIdsWithZoom.find(states.cameraId.c_str()) == cameraIdsWithZoom.end();
+ res = states.zoomRatioMappers[states.cameraId.c_str()].updateCaptureResult(
+ &captureResult.mMetadata, zoomRatioIs1);
+ if (res != OK) {
+ SET_ERR("Failed to update capture result zoom ratio metadata for frame %d: %s (%d)",
+ frameNumber, strerror(-res), res);
+ return;
+ }
+
+ // Fix up result metadata to account for rotateAndCrop in AUTO mode
+ if (rotateAndCropAuto) {
+ auto mapper = states.rotateAndCropMappers.find(states.cameraId.c_str());
+ if (mapper != states.rotateAndCropMappers.end()) {
+ res = mapper->second.updateCaptureResult(
+ &captureResult.mMetadata);
+ if (res != OK) {
+ SET_ERR("Unable to correct capture result rotate-and-crop for frame %d: %s (%d)",
+ frameNumber, strerror(-res), res);
+ return;
+ }
+ }
+ }
+
+ for (auto& physicalMetadata : captureResult.mPhysicalMetadatas) {
+ String8 cameraId8(physicalMetadata.mPhysicalCameraId);
+ auto mapper = states.distortionMappers.find(cameraId8.c_str());
+ if (mapper != states.distortionMappers.end()) {
+ res = mapper->second.correctCaptureResult(
+ &physicalMetadata.mPhysicalCameraMetadata);
+ if (res != OK) {
+ SET_ERR("Unable to correct physical capture result metadata for frame %d: %s (%d)",
+ frameNumber, strerror(-res), res);
+ return;
+ }
+ }
+
+ zoomRatioIs1 = cameraIdsWithZoom.find(cameraId8.c_str()) == cameraIdsWithZoom.end();
+ res = states.zoomRatioMappers[cameraId8.c_str()].updateCaptureResult(
+ &physicalMetadata.mPhysicalCameraMetadata, zoomRatioIs1);
+ if (res != OK) {
+ SET_ERR("Failed to update camera %s's physical zoom ratio metadata for "
+ "frame %d: %s(%d)", cameraId8.c_str(), frameNumber, strerror(-res), res);
+ return;
+ }
+ }
+
+ // Fix up result metadata for monochrome camera.
+ res = fixupMonochromeTags(states, states.deviceInfo, captureResult.mMetadata);
+ if (res != OK) {
+ SET_ERR("Failed to override result metadata: %s (%d)", strerror(-res), res);
+ return;
+ }
+ for (auto& physicalMetadata : captureResult.mPhysicalMetadatas) {
+ String8 cameraId8(physicalMetadata.mPhysicalCameraId);
+ res = fixupMonochromeTags(states,
+ states.physicalDeviceInfoMap.at(cameraId8.c_str()),
+ physicalMetadata.mPhysicalCameraMetadata);
+ if (res != OK) {
+ SET_ERR("Failed to override result metadata: %s (%d)", strerror(-res), res);
+ return;
+ }
+ }
+
+ std::unordered_map<std::string, CameraMetadata> monitoredPhysicalMetadata;
+ for (auto& m : physicalMetadatas) {
+ monitoredPhysicalMetadata.emplace(String8(m.mPhysicalCameraId).string(),
+ CameraMetadata(m.mPhysicalCameraMetadata));
+ }
+ states.tagMonitor.monitorMetadata(TagMonitor::RESULT,
+ frameNumber, timestamp.data.i64[0], captureResult.mMetadata,
+ monitoredPhysicalMetadata);
+
+ insertResultLocked(states, &captureResult, frameNumber);
+}
+
+// Reading one camera metadata from result argument via fmq or from the result
+// Assuming the fmq is protected by a lock already
+status_t readOneCameraMetadataLocked(
+ std::unique_ptr<ResultMetadataQueue>& fmq,
+ uint64_t fmqResultSize,
+ hardware::camera::device::V3_2::CameraMetadata& resultMetadata,
+ const hardware::camera::device::V3_2::CameraMetadata& result) {
+ if (fmqResultSize > 0) {
+ resultMetadata.resize(fmqResultSize);
+ if (fmq == nullptr) {
+ return NO_MEMORY; // logged in initialize()
+ }
+ if (!fmq->read(resultMetadata.data(), fmqResultSize)) {
+ ALOGE("%s: Cannot read camera metadata from fmq, size = %" PRIu64,
+ __FUNCTION__, fmqResultSize);
+ return INVALID_OPERATION;
+ }
+ } else {
+ resultMetadata.setToExternal(const_cast<uint8_t *>(result.data()),
+ result.size());
+ }
+
+ if (resultMetadata.size() != 0) {
+ status_t res;
+ const camera_metadata_t* metadata =
+ reinterpret_cast<const camera_metadata_t*>(resultMetadata.data());
+ size_t expected_metadata_size = resultMetadata.size();
+ if ((res = validate_camera_metadata_structure(metadata, &expected_metadata_size)) != OK) {
+ ALOGE("%s: Invalid camera metadata received by camera service from HAL: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return INVALID_OPERATION;
+ }
+ }
+
+ return OK;
+}
+
+void removeInFlightMapEntryLocked(CaptureOutputStates& states, int idx) {
+ ATRACE_CALL();
+ InFlightRequestMap& inflightMap = states.inflightMap;
+ nsecs_t duration = inflightMap.valueAt(idx).maxExpectedDuration;
+ inflightMap.removeItemsAt(idx, 1);
+
+ states.inflightIntf.onInflightEntryRemovedLocked(duration);
+}
+
+void removeInFlightRequestIfReadyLocked(CaptureOutputStates& states, int idx) {
+ InFlightRequestMap& inflightMap = states.inflightMap;
+ const InFlightRequest &request = inflightMap.valueAt(idx);
+ const uint32_t frameNumber = inflightMap.keyAt(idx);
+
+ nsecs_t sensorTimestamp = request.sensorTimestamp;
+ nsecs_t shutterTimestamp = request.shutterTimestamp;
+
+ // Check if it's okay to remove the request from InFlightMap:
+ // In the case of a successful request:
+ // all input and output buffers, all result metadata, shutter callback
+ // arrived.
+ // In the case of a unsuccessful request:
+ // all input and output buffers arrived.
+ if (request.numBuffersLeft == 0 &&
+ (request.skipResultMetadata ||
+ (request.haveResultMetadata && shutterTimestamp != 0))) {
+ if (request.stillCapture) {
+ ATRACE_ASYNC_END("still capture", frameNumber);
+ }
+
+ ATRACE_ASYNC_END("frame capture", frameNumber);
+
+ // Sanity check - if sensor timestamp matches shutter timestamp in the
+ // case of request having callback.
+ if (request.hasCallback && request.requestStatus == OK &&
+ sensorTimestamp != shutterTimestamp) {
+ SET_ERR("sensor timestamp (%" PRId64
+ ") for frame %d doesn't match shutter timestamp (%" PRId64 ")",
+ sensorTimestamp, frameNumber, shutterTimestamp);
+ }
+
+ // for an unsuccessful request, it may have pending output buffers to
+ // return.
+ assert(request.requestStatus != OK ||
+ request.pendingOutputBuffers.size() == 0);
+
+ returnOutputBuffers(
+ states.useHalBufManager, states.listener,
+ request.pendingOutputBuffers.array(),
+ request.pendingOutputBuffers.size(), 0, /*timestampIncreasing*/true,
+ request.outputSurfaces, request.resultExtras);
+
+ removeInFlightMapEntryLocked(states, idx);
+ ALOGVV("%s: removed frame %d from InFlightMap", __FUNCTION__, frameNumber);
+ }
+
+ states.inflightIntf.checkInflightMapLengthLocked();
+}
+
+void processCaptureResult(CaptureOutputStates& states, const camera3_capture_result *result) {
+ ATRACE_CALL();
+
+ status_t res;
+
+ uint32_t frameNumber = result->frame_number;
+ if (result->result == NULL && result->num_output_buffers == 0 &&
+ result->input_buffer == NULL) {
+ SET_ERR("No result data provided by HAL for frame %d",
+ frameNumber);
+ return;
+ }
+
+ if (!states.usePartialResult &&
+ result->result != NULL &&
+ result->partial_result != 1) {
+ SET_ERR("Result is malformed for frame %d: partial_result %u must be 1"
+ " if partial result is not supported",
+ frameNumber, result->partial_result);
+ return;
+ }
+
+ bool isPartialResult = false;
+ CameraMetadata collectedPartialResult;
+ bool hasInputBufferInRequest = false;
+
+ // Get shutter timestamp and resultExtras from list of in-flight requests,
+ // where it was added by the shutter notification for this frame. If the
+ // shutter timestamp isn't received yet, append the output buffers to the
+ // in-flight request and they will be returned when the shutter timestamp
+ // arrives. Update the in-flight status and remove the in-flight entry if
+ // all result data and shutter timestamp have been received.
+ nsecs_t shutterTimestamp = 0;
+ {
+ std::lock_guard<std::mutex> l(states.inflightLock);
+ ssize_t idx = states.inflightMap.indexOfKey(frameNumber);
+ if (idx == NAME_NOT_FOUND) {
+ SET_ERR("Unknown frame number for capture result: %d",
+ frameNumber);
+ return;
+ }
+ InFlightRequest &request = states.inflightMap.editValueAt(idx);
+ ALOGVV("%s: got InFlightRequest requestId = %" PRId32
+ ", frameNumber = %" PRId64 ", burstId = %" PRId32
+ ", partialResultCount = %d, hasCallback = %d",
+ __FUNCTION__, request.resultExtras.requestId,
+ request.resultExtras.frameNumber, request.resultExtras.burstId,
+ result->partial_result, request.hasCallback);
+ // Always update the partial count to the latest one if it's not 0
+ // (buffers only). When framework aggregates adjacent partial results
+ // into one, the latest partial count will be used.
+ if (result->partial_result != 0)
+ request.resultExtras.partialResultCount = result->partial_result;
+
+ // Check if this result carries only partial metadata
+ if (states.usePartialResult && result->result != NULL) {
+ if (result->partial_result > states.numPartialResults || result->partial_result < 1) {
+ SET_ERR("Result is malformed for frame %d: partial_result %u must be in"
+ " the range of [1, %d] when metadata is included in the result",
+ frameNumber, result->partial_result, states.numPartialResults);
+ return;
+ }
+ isPartialResult = (result->partial_result < states.numPartialResults);
+ if (isPartialResult && result->num_physcam_metadata) {
+ SET_ERR("Result is malformed for frame %d: partial_result not allowed for"
+ " physical camera result", frameNumber);
+ return;
+ }
+ if (isPartialResult) {
+ request.collectedPartialResult.append(result->result);
+ }
+
+ if (isPartialResult && request.hasCallback) {
+ // Send partial capture result
+ sendPartialCaptureResult(states, result->result, request.resultExtras,
+ frameNumber);
+ }
+ }
+
+ shutterTimestamp = request.shutterTimestamp;
+ hasInputBufferInRequest = request.hasInputBuffer;
+
+ // Did we get the (final) result metadata for this capture?
+ if (result->result != NULL && !isPartialResult) {
+ if (request.physicalCameraIds.size() != result->num_physcam_metadata) {
+ SET_ERR("Expected physical Camera metadata count %d not equal to actual count %d",
+ request.physicalCameraIds.size(), result->num_physcam_metadata);
+ return;
+ }
+ if (request.haveResultMetadata) {
+ SET_ERR("Called multiple times with metadata for frame %d",
+ frameNumber);
+ return;
+ }
+ for (uint32_t i = 0; i < result->num_physcam_metadata; i++) {
+ String8 physicalId(result->physcam_ids[i]);
+ std::set<String8>::iterator cameraIdIter =
+ request.physicalCameraIds.find(physicalId);
+ if (cameraIdIter != request.physicalCameraIds.end()) {
+ request.physicalCameraIds.erase(cameraIdIter);
+ } else {
+ SET_ERR("Total result for frame %d has already returned for camera %s",
+ frameNumber, physicalId.c_str());
+ return;
+ }
+ }
+ if (states.usePartialResult &&
+ !request.collectedPartialResult.isEmpty()) {
+ collectedPartialResult.acquire(
+ request.collectedPartialResult);
+ }
+ request.haveResultMetadata = true;
+ }
+
+ uint32_t numBuffersReturned = result->num_output_buffers;
+ if (result->input_buffer != NULL) {
+ if (hasInputBufferInRequest) {
+ numBuffersReturned += 1;
+ } else {
+ ALOGW("%s: Input buffer should be NULL if there is no input"
+ " buffer sent in the request",
+ __FUNCTION__);
+ }
+ }
+ request.numBuffersLeft -= numBuffersReturned;
+ if (request.numBuffersLeft < 0) {
+ SET_ERR("Too many buffers returned for frame %d",
+ frameNumber);
+ return;
+ }
+
+ camera_metadata_ro_entry_t entry;
+ res = find_camera_metadata_ro_entry(result->result,
+ ANDROID_SENSOR_TIMESTAMP, &entry);
+ if (res == OK && entry.count == 1) {
+ request.sensorTimestamp = entry.data.i64[0];
+ }
+
+ // If shutter event isn't received yet, append the output buffers to
+ // the in-flight request. Otherwise, return the output buffers to
+ // streams.
+ if (shutterTimestamp == 0) {
+ request.pendingOutputBuffers.appendArray(result->output_buffers,
+ result->num_output_buffers);
+ } else {
+ bool timestampIncreasing = !(request.zslCapture || request.hasInputBuffer);
+ returnOutputBuffers(states.useHalBufManager, states.listener,
+ result->output_buffers, result->num_output_buffers,
+ shutterTimestamp, timestampIncreasing,
+ request.outputSurfaces, request.resultExtras);
+ }
+
+ if (result->result != NULL && !isPartialResult) {
+ for (uint32_t i = 0; i < result->num_physcam_metadata; i++) {
+ CameraMetadata physicalMetadata;
+ physicalMetadata.append(result->physcam_metadata[i]);
+ request.physicalMetadatas.push_back({String16(result->physcam_ids[i]),
+ physicalMetadata});
+ }
+ if (shutterTimestamp == 0) {
+ request.pendingMetadata = result->result;
+ request.collectedPartialResult = collectedPartialResult;
+ } else if (request.hasCallback) {
+ CameraMetadata metadata;
+ metadata = result->result;
+ sendCaptureResult(states, metadata, request.resultExtras,
+ collectedPartialResult, frameNumber,
+ hasInputBufferInRequest, request.zslCapture && request.stillCapture,
+ request.rotateAndCropAuto, request.cameraIdsWithZoom,
+ request.physicalMetadatas);
+ }
+ }
+ removeInFlightRequestIfReadyLocked(states, idx);
+ } // scope for states.inFlightLock
+
+ if (result->input_buffer != NULL) {
+ if (hasInputBufferInRequest) {
+ Camera3Stream *stream =
+ Camera3Stream::cast(result->input_buffer->stream);
+ res = stream->returnInputBuffer(*(result->input_buffer));
+ // Note: stream may be deallocated at this point, if this buffer was the
+ // last reference to it.
+ if (res != OK) {
+ ALOGE("%s: RequestThread: Can't return input buffer for frame %d to"
+ " its stream:%s (%d)", __FUNCTION__,
+ frameNumber, strerror(-res), res);
+ }
+ } else {
+ ALOGW("%s: Input buffer should be NULL if there is no input"
+ " buffer sent in the request, skipping input buffer return.",
+ __FUNCTION__);
+ }
+ }
+}
+
+void processOneCaptureResultLocked(
+ CaptureOutputStates& states,
+ const hardware::camera::device::V3_2::CaptureResult& result,
+ const hardware::hidl_vec<
+ hardware::camera::device::V3_4::PhysicalCameraMetadata> physicalCameraMetadata) {
+ using hardware::camera::device::V3_2::StreamBuffer;
+ using hardware::camera::device::V3_2::BufferStatus;
+ std::unique_ptr<ResultMetadataQueue>& fmq = states.fmq;
+ BufferRecordsInterface& bufferRecords = states.bufferRecordsIntf;
+ camera3_capture_result r;
+ status_t res;
+ r.frame_number = result.frameNumber;
+
+ // Read and validate the result metadata.
+ hardware::camera::device::V3_2::CameraMetadata resultMetadata;
+ res = readOneCameraMetadataLocked(
+ fmq, result.fmqResultSize,
+ resultMetadata, result.result);
+ if (res != OK) {
+ ALOGE("%s: Frame %d: Failed to read capture result metadata",
+ __FUNCTION__, result.frameNumber);
+ return;
+ }
+ r.result = reinterpret_cast<const camera_metadata_t*>(resultMetadata.data());
+
+ // Read and validate physical camera metadata
+ size_t physResultCount = physicalCameraMetadata.size();
+ std::vector<const char*> physCamIds(physResultCount);
+ std::vector<const camera_metadata_t *> phyCamMetadatas(physResultCount);
+ std::vector<hardware::camera::device::V3_2::CameraMetadata> physResultMetadata;
+ physResultMetadata.resize(physResultCount);
+ for (size_t i = 0; i < physicalCameraMetadata.size(); i++) {
+ res = readOneCameraMetadataLocked(fmq, physicalCameraMetadata[i].fmqMetadataSize,
+ physResultMetadata[i], physicalCameraMetadata[i].metadata);
+ if (res != OK) {
+ ALOGE("%s: Frame %d: Failed to read capture result metadata for camera %s",
+ __FUNCTION__, result.frameNumber,
+ physicalCameraMetadata[i].physicalCameraId.c_str());
+ return;
+ }
+ physCamIds[i] = physicalCameraMetadata[i].physicalCameraId.c_str();
+ phyCamMetadatas[i] = reinterpret_cast<const camera_metadata_t*>(
+ physResultMetadata[i].data());
+ }
+ r.num_physcam_metadata = physResultCount;
+ r.physcam_ids = physCamIds.data();
+ r.physcam_metadata = phyCamMetadatas.data();
+
+ std::vector<camera3_stream_buffer_t> outputBuffers(result.outputBuffers.size());
+ std::vector<buffer_handle_t> outputBufferHandles(result.outputBuffers.size());
+ for (size_t i = 0; i < result.outputBuffers.size(); i++) {
+ auto& bDst = outputBuffers[i];
+ const StreamBuffer &bSrc = result.outputBuffers[i];
+
+ sp<Camera3StreamInterface> stream = states.outputStreams.get(bSrc.streamId);
+ if (stream == nullptr) {
+ ALOGE("%s: Frame %d: Buffer %zu: Invalid output stream id %d",
+ __FUNCTION__, result.frameNumber, i, bSrc.streamId);
+ return;
+ }
+ bDst.stream = stream->asHalStream();
+
+ bool noBufferReturned = false;
+ buffer_handle_t *buffer = nullptr;
+ if (states.useHalBufManager) {
+ // This is suspicious most of the time but can be correct during flush where HAL
+ // has to return capture result before a buffer is requested
+ if (bSrc.bufferId == BUFFER_ID_NO_BUFFER) {
+ if (bSrc.status == BufferStatus::OK) {
+ ALOGE("%s: Frame %d: Buffer %zu: No bufferId for stream %d",
+ __FUNCTION__, result.frameNumber, i, bSrc.streamId);
+ // Still proceeds so other buffers can be returned
+ }
+ noBufferReturned = true;
+ }
+ if (noBufferReturned) {
+ res = OK;
+ } else {
+ res = bufferRecords.popInflightRequestBuffer(bSrc.bufferId, &buffer);
+ }
+ } else {
+ res = bufferRecords.popInflightBuffer(result.frameNumber, bSrc.streamId, &buffer);
+ }
+
+ if (res != OK) {
+ ALOGE("%s: Frame %d: Buffer %zu: No in-flight buffer for stream %d",
+ __FUNCTION__, result.frameNumber, i, bSrc.streamId);
+ return;
+ }
+
+ bDst.buffer = buffer;
+ bDst.status = mapHidlBufferStatus(bSrc.status);
+ bDst.acquire_fence = -1;
+ if (bSrc.releaseFence == nullptr) {
+ bDst.release_fence = -1;
+ } else if (bSrc.releaseFence->numFds == 1) {
+ if (noBufferReturned) {
+ ALOGE("%s: got releaseFence without output buffer!", __FUNCTION__);
+ }
+ bDst.release_fence = dup(bSrc.releaseFence->data[0]);
+ } else {
+ ALOGE("%s: Frame %d: Invalid release fence for buffer %zu, fd count is %d, not 1",
+ __FUNCTION__, result.frameNumber, i, bSrc.releaseFence->numFds);
+ return;
+ }
+ }
+ r.num_output_buffers = outputBuffers.size();
+ r.output_buffers = outputBuffers.data();
+
+ camera3_stream_buffer_t inputBuffer;
+ if (result.inputBuffer.streamId == -1) {
+ r.input_buffer = nullptr;
+ } else {
+ if (states.inputStream->getId() != result.inputBuffer.streamId) {
+ ALOGE("%s: Frame %d: Invalid input stream id %d", __FUNCTION__,
+ result.frameNumber, result.inputBuffer.streamId);
+ return;
+ }
+ inputBuffer.stream = states.inputStream->asHalStream();
+ buffer_handle_t *buffer;
+ res = bufferRecords.popInflightBuffer(result.frameNumber, result.inputBuffer.streamId,
+ &buffer);
+ if (res != OK) {
+ ALOGE("%s: Frame %d: Input buffer: No in-flight buffer for stream %d",
+ __FUNCTION__, result.frameNumber, result.inputBuffer.streamId);
+ return;
+ }
+ inputBuffer.buffer = buffer;
+ inputBuffer.status = mapHidlBufferStatus(result.inputBuffer.status);
+ inputBuffer.acquire_fence = -1;
+ if (result.inputBuffer.releaseFence == nullptr) {
+ inputBuffer.release_fence = -1;
+ } else if (result.inputBuffer.releaseFence->numFds == 1) {
+ inputBuffer.release_fence = dup(result.inputBuffer.releaseFence->data[0]);
+ } else {
+ ALOGE("%s: Frame %d: Invalid release fence for input buffer, fd count is %d, not 1",
+ __FUNCTION__, result.frameNumber, result.inputBuffer.releaseFence->numFds);
+ return;
+ }
+ r.input_buffer = &inputBuffer;
+ }
+
+ r.partial_result = result.partialResult;
+
+ processCaptureResult(states, &r);
+}
+
+void returnOutputBuffers(
+ bool useHalBufManager,
+ sp<NotificationListener> listener,
+ const camera3_stream_buffer_t *outputBuffers, size_t numBuffers,
+ nsecs_t timestamp, bool timestampIncreasing,
+ const SurfaceMap& outputSurfaces,
+ const CaptureResultExtras &inResultExtras) {
+
+ for (size_t i = 0; i < numBuffers; i++)
+ {
+ if (outputBuffers[i].buffer == nullptr) {
+ if (!useHalBufManager) {
+ // With HAL buffer management API, HAL sometimes will have to return buffers that
+ // has not got a output buffer handle filled yet. This is though illegal if HAL
+ // buffer management API is not being used.
+ ALOGE("%s: cannot return a null buffer!", __FUNCTION__);
+ }
+ continue;
+ }
+
+ Camera3StreamInterface *stream = Camera3Stream::cast(outputBuffers[i].stream);
+ int streamId = stream->getId();
+ const auto& it = outputSurfaces.find(streamId);
+ status_t res = OK;
+ if (it != outputSurfaces.end()) {
+ res = stream->returnBuffer(
+ outputBuffers[i], timestamp, timestampIncreasing, it->second,
+ inResultExtras.frameNumber);
+ } else {
+ res = stream->returnBuffer(
+ outputBuffers[i], timestamp, timestampIncreasing, std::vector<size_t> (),
+ inResultExtras.frameNumber);
+ }
+
+ // Note: stream may be deallocated at this point, if this buffer was
+ // the last reference to it.
+ if (res == NO_INIT || res == DEAD_OBJECT) {
+ ALOGV("Can't return buffer to its stream: %s (%d)", strerror(-res), res);
+ } else if (res != OK) {
+ ALOGE("Can't return buffer to its stream: %s (%d)", strerror(-res), res);
+ }
+
+ // Long processing consumers can cause returnBuffer timeout for shared stream
+ // If that happens, cancel the buffer and send a buffer error to client
+ if (it != outputSurfaces.end() && res == TIMED_OUT &&
+ outputBuffers[i].status == CAMERA3_BUFFER_STATUS_OK) {
+ // cancel the buffer
+ camera3_stream_buffer_t sb = outputBuffers[i];
+ sb.status = CAMERA3_BUFFER_STATUS_ERROR;
+ stream->returnBuffer(sb, /*timestamp*/0, timestampIncreasing, std::vector<size_t> (),
+ inResultExtras.frameNumber);
+
+ if (listener != nullptr) {
+ CaptureResultExtras extras = inResultExtras;
+ extras.errorStreamId = streamId;
+ listener->notifyError(
+ hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER,
+ extras);
+ }
+ }
+ }
+}
+
+void notifyShutter(CaptureOutputStates& states, const camera3_shutter_msg_t &msg) {
+ ATRACE_CALL();
+ ssize_t idx;
+
+ // Set timestamp for the request in the in-flight tracking
+ // and get the request ID to send upstream
+ {
+ std::lock_guard<std::mutex> l(states.inflightLock);
+ InFlightRequestMap& inflightMap = states.inflightMap;
+ idx = inflightMap.indexOfKey(msg.frame_number);
+ if (idx >= 0) {
+ InFlightRequest &r = inflightMap.editValueAt(idx);
+
+ // Verify ordering of shutter notifications
+ {
+ std::lock_guard<std::mutex> l(states.outputLock);
+ // TODO: need to track errors for tighter bounds on expected frame number.
+ if (r.hasInputBuffer) {
+ if (msg.frame_number < states.nextReprocShutterFrameNum) {
+ SET_ERR("Reprocess shutter notification out-of-order. Expected "
+ "notification for frame %d, got frame %d",
+ states.nextReprocShutterFrameNum, msg.frame_number);
+ return;
+ }
+ states.nextReprocShutterFrameNum = msg.frame_number + 1;
+ } else if (r.zslCapture && r.stillCapture) {
+ if (msg.frame_number < states.nextZslShutterFrameNum) {
+ SET_ERR("ZSL still capture shutter notification out-of-order. Expected "
+ "notification for frame %d, got frame %d",
+ states.nextZslShutterFrameNum, msg.frame_number);
+ return;
+ }
+ states.nextZslShutterFrameNum = msg.frame_number + 1;
+ } else {
+ if (msg.frame_number < states.nextShutterFrameNum) {
+ SET_ERR("Shutter notification out-of-order. Expected "
+ "notification for frame %d, got frame %d",
+ states.nextShutterFrameNum, msg.frame_number);
+ return;
+ }
+ states.nextShutterFrameNum = msg.frame_number + 1;
+ }
+ }
+
+ r.shutterTimestamp = msg.timestamp;
+ if (r.hasCallback) {
+ ALOGVV("Camera %s: %s: Shutter fired for frame %d (id %d) at %" PRId64,
+ states.cameraId.string(), __FUNCTION__,
+ msg.frame_number, r.resultExtras.requestId, msg.timestamp);
+ // Call listener, if any
+ if (states.listener != nullptr) {
+ states.listener->notifyShutter(r.resultExtras, msg.timestamp);
+ }
+ // send pending result and buffers
+ sendCaptureResult(states,
+ r.pendingMetadata, r.resultExtras,
+ r.collectedPartialResult, msg.frame_number,
+ r.hasInputBuffer, r.zslCapture && r.stillCapture,
+ r.rotateAndCropAuto, r.cameraIdsWithZoom, r.physicalMetadatas);
+ }
+ bool timestampIncreasing = !(r.zslCapture || r.hasInputBuffer);
+ returnOutputBuffers(
+ states.useHalBufManager, states.listener,
+ r.pendingOutputBuffers.array(),
+ r.pendingOutputBuffers.size(), r.shutterTimestamp, timestampIncreasing,
+ r.outputSurfaces, r.resultExtras);
+ r.pendingOutputBuffers.clear();
+
+ removeInFlightRequestIfReadyLocked(states, idx);
+ }
+ }
+ if (idx < 0) {
+ SET_ERR("Shutter notification for non-existent frame number %d",
+ msg.frame_number);
+ }
+}
+
+void notifyError(CaptureOutputStates& states, const camera3_error_msg_t &msg) {
+ ATRACE_CALL();
+ // Map camera HAL error codes to ICameraDeviceCallback error codes
+ // Index into this with the HAL error code
+ static const int32_t halErrorMap[CAMERA3_MSG_NUM_ERRORS] = {
+ // 0 = Unused error code
+ hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_INVALID_ERROR,
+ // 1 = CAMERA3_MSG_ERROR_DEVICE
+ hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DEVICE,
+ // 2 = CAMERA3_MSG_ERROR_REQUEST
+ hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST,
+ // 3 = CAMERA3_MSG_ERROR_RESULT
+ hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_RESULT,
+ // 4 = CAMERA3_MSG_ERROR_BUFFER
+ hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER
+ };
+
+ int32_t errorCode =
+ ((msg.error_code >= 0) &&
+ (msg.error_code < CAMERA3_MSG_NUM_ERRORS)) ?
+ halErrorMap[msg.error_code] :
+ hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_INVALID_ERROR;
+
+ int streamId = 0;
+ String16 physicalCameraId;
+ if (msg.error_stream != nullptr) {
+ Camera3Stream *stream =
+ Camera3Stream::cast(msg.error_stream);
+ streamId = stream->getId();
+ physicalCameraId = String16(stream->physicalCameraId());
+ }
+ ALOGV("Camera %s: %s: HAL error, frame %d, stream %d: %d",
+ states.cameraId.string(), __FUNCTION__, msg.frame_number,
+ streamId, msg.error_code);
+
+ CaptureResultExtras resultExtras;
+ switch (errorCode) {
+ case hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DEVICE:
+ // SET_ERR calls into listener to notify application
+ SET_ERR("Camera HAL reported serious device error");
+ break;
+ case hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST:
+ case hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_RESULT:
+ case hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER:
+ {
+ std::lock_guard<std::mutex> l(states.inflightLock);
+ ssize_t idx = states.inflightMap.indexOfKey(msg.frame_number);
+ if (idx >= 0) {
+ InFlightRequest &r = states.inflightMap.editValueAt(idx);
+ r.requestStatus = msg.error_code;
+ resultExtras = r.resultExtras;
+ bool logicalDeviceResultError = false;
+ if (hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_RESULT ==
+ errorCode) {
+ if (physicalCameraId.size() > 0) {
+ String8 cameraId(physicalCameraId);
+ auto iter = r.physicalCameraIds.find(cameraId);
+ if (iter == r.physicalCameraIds.end()) {
+ ALOGE("%s: Reported result failure for physical camera device: %s "
+ " which is not part of the respective request!",
+ __FUNCTION__, cameraId.string());
+ break;
+ }
+ r.physicalCameraIds.erase(iter);
+ resultExtras.errorPhysicalCameraId = physicalCameraId;
+ } else {
+ logicalDeviceResultError = true;
+ }
+ }
+
+ if (logicalDeviceResultError
+ || hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST ==
+ errorCode) {
+ r.skipResultMetadata = true;
+ }
+ if (logicalDeviceResultError) {
+ // In case of missing result check whether the buffers
+ // returned. If they returned, then remove inflight
+ // request.
+ // TODO: should we call this for ERROR_CAMERA_REQUEST as well?
+ // otherwise we are depending on HAL to send the buffers back after
+ // calling notifyError. Not sure if that's in the spec.
+ removeInFlightRequestIfReadyLocked(states, idx);
+ }
+ } else {
+ resultExtras.frameNumber = msg.frame_number;
+ ALOGE("Camera %s: %s: cannot find in-flight request on "
+ "frame %" PRId64 " error", states.cameraId.string(), __FUNCTION__,
+ resultExtras.frameNumber);
+ }
+ }
+ resultExtras.errorStreamId = streamId;
+ if (states.listener != nullptr) {
+ states.listener->notifyError(errorCode, resultExtras);
+ } else {
+ ALOGE("Camera %s: %s: no listener available",
+ states.cameraId.string(), __FUNCTION__);
+ }
+ break;
+ default:
+ // SET_ERR calls notifyError
+ SET_ERR("Unknown error message from HAL: %d", msg.error_code);
+ break;
+ }
+}
+
+void notify(CaptureOutputStates& states, const camera3_notify_msg *msg) {
+ switch (msg->type) {
+ case CAMERA3_MSG_ERROR: {
+ notifyError(states, msg->message.error);
+ break;
+ }
+ case CAMERA3_MSG_SHUTTER: {
+ notifyShutter(states, msg->message.shutter);
+ break;
+ }
+ default:
+ SET_ERR("Unknown notify message from HAL: %d",
+ msg->type);
+ }
+}
+
+void notify(CaptureOutputStates& states,
+ const hardware::camera::device::V3_2::NotifyMsg& msg) {
+ using android::hardware::camera::device::V3_2::MsgType;
+ using android::hardware::camera::device::V3_2::ErrorCode;
+
+ ATRACE_CALL();
+ camera3_notify_msg m;
+ switch (msg.type) {
+ case MsgType::ERROR:
+ m.type = CAMERA3_MSG_ERROR;
+ m.message.error.frame_number = msg.msg.error.frameNumber;
+ if (msg.msg.error.errorStreamId >= 0) {
+ sp<Camera3StreamInterface> stream =
+ states.outputStreams.get(msg.msg.error.errorStreamId);
+ if (stream == nullptr) {
+ ALOGE("%s: Frame %d: Invalid error stream id %d", __FUNCTION__,
+ m.message.error.frame_number, msg.msg.error.errorStreamId);
+ return;
+ }
+ m.message.error.error_stream = stream->asHalStream();
+ } else {
+ m.message.error.error_stream = nullptr;
+ }
+ switch (msg.msg.error.errorCode) {
+ case ErrorCode::ERROR_DEVICE:
+ m.message.error.error_code = CAMERA3_MSG_ERROR_DEVICE;
+ break;
+ case ErrorCode::ERROR_REQUEST:
+ m.message.error.error_code = CAMERA3_MSG_ERROR_REQUEST;
+ break;
+ case ErrorCode::ERROR_RESULT:
+ m.message.error.error_code = CAMERA3_MSG_ERROR_RESULT;
+ break;
+ case ErrorCode::ERROR_BUFFER:
+ m.message.error.error_code = CAMERA3_MSG_ERROR_BUFFER;
+ break;
+ }
+ break;
+ case MsgType::SHUTTER:
+ m.type = CAMERA3_MSG_SHUTTER;
+ m.message.shutter.frame_number = msg.msg.shutter.frameNumber;
+ m.message.shutter.timestamp = msg.msg.shutter.timestamp;
+ break;
+ }
+ notify(states, &m);
+}
+
+void requestStreamBuffers(RequestBufferStates& states,
+ const hardware::hidl_vec<hardware::camera::device::V3_5::BufferRequest>& bufReqs,
+ hardware::camera::device::V3_5::ICameraDeviceCallback::requestStreamBuffers_cb _hidl_cb) {
+ using android::hardware::camera::device::V3_2::BufferStatus;
+ using android::hardware::camera::device::V3_2::StreamBuffer;
+ using android::hardware::camera::device::V3_5::BufferRequestStatus;
+ using android::hardware::camera::device::V3_5::StreamBufferRet;
+ using android::hardware::camera::device::V3_5::StreamBufferRequestError;
+
+ std::lock_guard<std::mutex> lock(states.reqBufferLock);
+
+ hardware::hidl_vec<StreamBufferRet> bufRets;
+ if (!states.useHalBufManager) {
+ ALOGE("%s: Camera %s does not support HAL buffer management",
+ __FUNCTION__, states.cameraId.string());
+ _hidl_cb(BufferRequestStatus::FAILED_ILLEGAL_ARGUMENTS, bufRets);
+ return;
+ }
+
+ SortedVector<int32_t> streamIds;
+ ssize_t sz = streamIds.setCapacity(bufReqs.size());
+ if (sz < 0 || static_cast<size_t>(sz) != bufReqs.size()) {
+ ALOGE("%s: failed to allocate memory for %zu buffer requests",
+ __FUNCTION__, bufReqs.size());
+ _hidl_cb(BufferRequestStatus::FAILED_ILLEGAL_ARGUMENTS, bufRets);
+ return;
+ }
+
+ if (bufReqs.size() > states.outputStreams.size()) {
+ ALOGE("%s: too many buffer requests (%zu > # of output streams %zu)",
+ __FUNCTION__, bufReqs.size(), states.outputStreams.size());
+ _hidl_cb(BufferRequestStatus::FAILED_ILLEGAL_ARGUMENTS, bufRets);
+ return;
+ }
+
+ // Check for repeated streamId
+ for (const auto& bufReq : bufReqs) {
+ if (streamIds.indexOf(bufReq.streamId) != NAME_NOT_FOUND) {
+ ALOGE("%s: Stream %d appear multiple times in buffer requests",
+ __FUNCTION__, bufReq.streamId);
+ _hidl_cb(BufferRequestStatus::FAILED_ILLEGAL_ARGUMENTS, bufRets);
+ return;
+ }
+ streamIds.add(bufReq.streamId);
+ }
+
+ if (!states.reqBufferIntf.startRequestBuffer()) {
+ ALOGE("%s: request buffer disallowed while camera service is configuring",
+ __FUNCTION__);
+ _hidl_cb(BufferRequestStatus::FAILED_CONFIGURING, bufRets);
+ return;
+ }
+
+ bufRets.resize(bufReqs.size());
+
+ bool allReqsSucceeds = true;
+ bool oneReqSucceeds = false;
+ for (size_t i = 0; i < bufReqs.size(); i++) {
+ const auto& bufReq = bufReqs[i];
+ auto& bufRet = bufRets[i];
+ int32_t streamId = bufReq.streamId;
+ sp<Camera3OutputStreamInterface> outputStream = states.outputStreams.get(streamId);
+ if (outputStream == nullptr) {
+ ALOGE("%s: Output stream id %d not found!", __FUNCTION__, streamId);
+ hardware::hidl_vec<StreamBufferRet> emptyBufRets;
+ _hidl_cb(BufferRequestStatus::FAILED_ILLEGAL_ARGUMENTS, emptyBufRets);
+ states.reqBufferIntf.endRequestBuffer();
+ return;
+ }
+
+ if (outputStream->isAbandoned()) {
+ bufRet.val.error(StreamBufferRequestError::STREAM_DISCONNECTED);
+ allReqsSucceeds = false;
+ continue;
+ }
+
+ bufRet.streamId = streamId;
+ size_t handOutBufferCount = outputStream->getOutstandingBuffersCount();
+ uint32_t numBuffersRequested = bufReq.numBuffersRequested;
+ size_t totalHandout = handOutBufferCount + numBuffersRequested;
+ uint32_t maxBuffers = outputStream->asHalStream()->max_buffers;
+ if (totalHandout > maxBuffers) {
+ // Not able to allocate enough buffer. Exit early for this stream
+ ALOGE("%s: request too much buffers for stream %d: at HAL: %zu + requesting: %d"
+ " > max: %d", __FUNCTION__, streamId, handOutBufferCount,
+ numBuffersRequested, maxBuffers);
+ bufRet.val.error(StreamBufferRequestError::MAX_BUFFER_EXCEEDED);
+ allReqsSucceeds = false;
+ continue;
+ }
+
+ hardware::hidl_vec<StreamBuffer> tmpRetBuffers(numBuffersRequested);
+ bool currentReqSucceeds = true;
+ std::vector<camera3_stream_buffer_t> streamBuffers(numBuffersRequested);
+ size_t numAllocatedBuffers = 0;
+ size_t numPushedInflightBuffers = 0;
+ for (size_t b = 0; b < numBuffersRequested; b++) {
+ camera3_stream_buffer_t& sb = streamBuffers[b];
+ // Since this method can run concurrently with request thread
+ // We need to update the wait duration everytime we call getbuffer
+ nsecs_t waitDuration = states.reqBufferIntf.getWaitDuration();
+ status_t res = outputStream->getBuffer(&sb, waitDuration);
+ if (res != OK) {
+ if (res == NO_INIT || res == DEAD_OBJECT) {
+ ALOGV("%s: Can't get output buffer for stream %d: %s (%d)",
+ __FUNCTION__, streamId, strerror(-res), res);
+ bufRet.val.error(StreamBufferRequestError::STREAM_DISCONNECTED);
+ } else {
+ ALOGE("%s: Can't get output buffer for stream %d: %s (%d)",
+ __FUNCTION__, streamId, strerror(-res), res);
+ if (res == TIMED_OUT || res == NO_MEMORY) {
+ bufRet.val.error(StreamBufferRequestError::NO_BUFFER_AVAILABLE);
+ } else {
+ bufRet.val.error(StreamBufferRequestError::UNKNOWN_ERROR);
+ }
+ }
+ currentReqSucceeds = false;
+ break;
+ }
+ numAllocatedBuffers++;
+
+ buffer_handle_t *buffer = sb.buffer;
+ auto pair = states.bufferRecordsIntf.getBufferId(*buffer, streamId);
+ bool isNewBuffer = pair.first;
+ uint64_t bufferId = pair.second;
+ StreamBuffer& hBuf = tmpRetBuffers[b];
+
+ hBuf.streamId = streamId;
+ hBuf.bufferId = bufferId;
+ hBuf.buffer = (isNewBuffer) ? *buffer : nullptr;
+ hBuf.status = BufferStatus::OK;
+ hBuf.releaseFence = nullptr;
+
+ native_handle_t *acquireFence = nullptr;
+ if (sb.acquire_fence != -1) {
+ acquireFence = native_handle_create(1,0);
+ acquireFence->data[0] = sb.acquire_fence;
+ }
+ hBuf.acquireFence.setTo(acquireFence, /*shouldOwn*/true);
+ hBuf.releaseFence = nullptr;
+
+ res = states.bufferRecordsIntf.pushInflightRequestBuffer(bufferId, buffer, streamId);
+ if (res != OK) {
+ ALOGE("%s: Can't get register request buffers for stream %d: %s (%d)",
+ __FUNCTION__, streamId, strerror(-res), res);
+ bufRet.val.error(StreamBufferRequestError::UNKNOWN_ERROR);
+ currentReqSucceeds = false;
+ break;
+ }
+ numPushedInflightBuffers++;
+ }
+ if (currentReqSucceeds) {
+ bufRet.val.buffers(std::move(tmpRetBuffers));
+ oneReqSucceeds = true;
+ } else {
+ allReqsSucceeds = false;
+ for (size_t b = 0; b < numPushedInflightBuffers; b++) {
+ StreamBuffer& hBuf = tmpRetBuffers[b];
+ buffer_handle_t* buffer;
+ status_t res = states.bufferRecordsIntf.popInflightRequestBuffer(
+ hBuf.bufferId, &buffer);
+ if (res != OK) {
+ SET_ERR("%s: popInflightRequestBuffer failed for stream %d: %s (%d)",
+ __FUNCTION__, streamId, strerror(-res), res);
+ }
+ }
+ for (size_t b = 0; b < numAllocatedBuffers; b++) {
+ camera3_stream_buffer_t& sb = streamBuffers[b];
+ sb.acquire_fence = -1;
+ sb.status = CAMERA3_BUFFER_STATUS_ERROR;
+ }
+ returnOutputBuffers(states.useHalBufManager, /*listener*/nullptr,
+ streamBuffers.data(), numAllocatedBuffers, 0);
+ }
+ }
+
+ _hidl_cb(allReqsSucceeds ? BufferRequestStatus::OK :
+ oneReqSucceeds ? BufferRequestStatus::FAILED_PARTIAL :
+ BufferRequestStatus::FAILED_UNKNOWN,
+ bufRets);
+ states.reqBufferIntf.endRequestBuffer();
+}
+
+void returnStreamBuffers(ReturnBufferStates& states,
+ const hardware::hidl_vec<hardware::camera::device::V3_2::StreamBuffer>& buffers) {
+ if (!states.useHalBufManager) {
+ ALOGE("%s: Camera %s does not support HAL buffer managerment",
+ __FUNCTION__, states.cameraId.string());
+ return;
+ }
+
+ for (const auto& buf : buffers) {
+ if (buf.bufferId == BUFFER_ID_NO_BUFFER) {
+ ALOGE("%s: cannot return a buffer without bufferId", __FUNCTION__);
+ continue;
+ }
+
+ buffer_handle_t* buffer;
+ status_t res = states.bufferRecordsIntf.popInflightRequestBuffer(buf.bufferId, &buffer);
+
+ if (res != OK) {
+ ALOGE("%s: cannot find in-flight buffer %" PRIu64 " for stream %d",
+ __FUNCTION__, buf.bufferId, buf.streamId);
+ continue;
+ }
+
+ camera3_stream_buffer_t streamBuffer;
+ streamBuffer.buffer = buffer;
+ streamBuffer.status = CAMERA3_BUFFER_STATUS_ERROR;
+ streamBuffer.acquire_fence = -1;
+ streamBuffer.release_fence = -1;
+
+ if (buf.releaseFence == nullptr) {
+ streamBuffer.release_fence = -1;
+ } else if (buf.releaseFence->numFds == 1) {
+ streamBuffer.release_fence = dup(buf.releaseFence->data[0]);
+ } else {
+ ALOGE("%s: Invalid release fence, fd count is %d, not 1",
+ __FUNCTION__, buf.releaseFence->numFds);
+ continue;
+ }
+
+ sp<Camera3StreamInterface> stream = states.outputStreams.get(buf.streamId);
+ if (stream == nullptr) {
+ ALOGE("%s: Output stream id %d not found!", __FUNCTION__, buf.streamId);
+ continue;
+ }
+ streamBuffer.stream = stream->asHalStream();
+ returnOutputBuffers(states.useHalBufManager, /*listener*/nullptr,
+ &streamBuffer, /*size*/1, /*timestamp*/ 0);
+ }
+}
+
+void flushInflightRequests(FlushInflightReqStates& states) {
+ ATRACE_CALL();
+ { // First return buffers cached in mInFlightMap
+ std::lock_guard<std::mutex> l(states.inflightLock);
+ for (size_t idx = 0; idx < states.inflightMap.size(); idx++) {
+ const InFlightRequest &request = states.inflightMap.valueAt(idx);
+ returnOutputBuffers(
+ states.useHalBufManager, states.listener,
+ request.pendingOutputBuffers.array(),
+ request.pendingOutputBuffers.size(), 0,
+ /*timestampIncreasing*/true, request.outputSurfaces,
+ request.resultExtras);
+ }
+ states.inflightMap.clear();
+ states.inflightIntf.onInflightMapFlushedLocked();
+ }
+
+ // Then return all inflight buffers not returned by HAL
+ std::vector<std::pair<int32_t, int32_t>> inflightKeys;
+ states.flushBufferIntf.getInflightBufferKeys(&inflightKeys);
+
+ // Inflight buffers for HAL buffer manager
+ std::vector<uint64_t> inflightRequestBufferKeys;
+ states.flushBufferIntf.getInflightRequestBufferKeys(&inflightRequestBufferKeys);
+
+ // (streamId, frameNumber, buffer_handle_t*) tuple for all inflight buffers.
+ // frameNumber will be -1 for buffers from HAL buffer manager
+ std::vector<std::tuple<int32_t, int32_t, buffer_handle_t*>> inflightBuffers;
+ inflightBuffers.reserve(inflightKeys.size() + inflightRequestBufferKeys.size());
+
+ for (auto& pair : inflightKeys) {
+ int32_t frameNumber = pair.first;
+ int32_t streamId = pair.second;
+ buffer_handle_t* buffer;
+ status_t res = states.bufferRecordsIntf.popInflightBuffer(frameNumber, streamId, &buffer);
+ if (res != OK) {
+ ALOGE("%s: Frame %d: No in-flight buffer for stream %d",
+ __FUNCTION__, frameNumber, streamId);
+ continue;
+ }
+ inflightBuffers.push_back(std::make_tuple(streamId, frameNumber, buffer));
+ }
+
+ for (auto& bufferId : inflightRequestBufferKeys) {
+ int32_t streamId = -1;
+ buffer_handle_t* buffer = nullptr;
+ status_t res = states.bufferRecordsIntf.popInflightRequestBuffer(
+ bufferId, &buffer, &streamId);
+ if (res != OK) {
+ ALOGE("%s: cannot find in-flight buffer %" PRIu64, __FUNCTION__, bufferId);
+ continue;
+ }
+ inflightBuffers.push_back(std::make_tuple(streamId, /*frameNumber*/-1, buffer));
+ }
+
+ std::vector<sp<Camera3StreamInterface>> streams = states.flushBufferIntf.getAllStreams();
+
+ for (auto& tuple : inflightBuffers) {
+ status_t res = OK;
+ int32_t streamId = std::get<0>(tuple);
+ int32_t frameNumber = std::get<1>(tuple);
+ buffer_handle_t* buffer = std::get<2>(tuple);
+
+ camera3_stream_buffer_t streamBuffer;
+ streamBuffer.buffer = buffer;
+ streamBuffer.status = CAMERA3_BUFFER_STATUS_ERROR;
+ streamBuffer.acquire_fence = -1;
+ streamBuffer.release_fence = -1;
+
+ for (auto& stream : streams) {
+ if (streamId == stream->getId()) {
+ // Return buffer to deleted stream
+ camera3_stream* halStream = stream->asHalStream();
+ streamBuffer.stream = halStream;
+ switch (halStream->stream_type) {
+ case CAMERA3_STREAM_OUTPUT:
+ res = stream->returnBuffer(streamBuffer, /*timestamp*/ 0,
+ /*timestampIncreasing*/true, std::vector<size_t> (), frameNumber);
+ if (res != OK) {
+ ALOGE("%s: Can't return output buffer for frame %d to"
+ " stream %d: %s (%d)", __FUNCTION__,
+ frameNumber, streamId, strerror(-res), res);
+ }
+ break;
+ case CAMERA3_STREAM_INPUT:
+ res = stream->returnInputBuffer(streamBuffer);
+ if (res != OK) {
+ ALOGE("%s: Can't return input buffer for frame %d to"
+ " stream %d: %s (%d)", __FUNCTION__,
+ frameNumber, streamId, strerror(-res), res);
+ }
+ break;
+ default: // Bi-direcitonal stream is deprecated
+ ALOGE("%s: stream %d has unknown stream type %d",
+ __FUNCTION__, streamId, halStream->stream_type);
+ break;
+ }
+ break;
+ }
+ }
+ }
+}
+
+} // camera3
+} // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3OutputUtils.h b/services/camera/libcameraservice/device3/Camera3OutputUtils.h
new file mode 100644
index 0000000..300df5b
--- /dev/null
+++ b/services/camera/libcameraservice/device3/Camera3OutputUtils.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SERVERS_CAMERA3_OUTPUT_UTILS_H
+#define ANDROID_SERVERS_CAMERA3_OUTPUT_UTILS_H
+
+#include <memory>
+#include <mutex>
+
+#include <cutils/native_handle.h>
+
+#include <fmq/MessageQueue.h>
+
+#include <common/CameraDeviceBase.h>
+
+#include "device3/BufferUtils.h"
+#include "device3/DistortionMapper.h"
+#include "device3/ZoomRatioMapper.h"
+#include "device3/RotateAndCropMapper.h"
+#include "device3/InFlightRequest.h"
+#include "device3/Camera3Stream.h"
+#include "device3/Camera3OutputStreamInterface.h"
+#include "utils/TagMonitor.h"
+
+namespace android {
+
+using ResultMetadataQueue = hardware::MessageQueue<uint8_t, hardware::kSynchronizedReadWrite>;
+
+namespace camera3 {
+
+ /**
+ * Helper methods shared between Camera3Device/Camera3OfflineSession for HAL callbacks
+ */
+ // helper function to return the output buffers to output streams.
+ void returnOutputBuffers(
+ bool useHalBufManager,
+ sp<NotificationListener> listener, // Only needed when outputSurfaces is not empty
+ const camera3_stream_buffer_t *outputBuffers,
+ size_t numBuffers, nsecs_t timestamp, bool timestampIncreasing = true,
+ // The following arguments are only meant for surface sharing use case
+ const SurfaceMap& outputSurfaces = SurfaceMap{},
+ // Used to send buffer error callback when failing to return buffer
+ const CaptureResultExtras &resultExtras = CaptureResultExtras{});
+
+ // Camera3Device/Camera3OfflineSession internal states used in notify/processCaptureResult
+ // callbacks
+ struct CaptureOutputStates {
+ const String8& cameraId;
+ std::mutex& inflightLock;
+ InFlightRequestMap& inflightMap; // end of inflightLock scope
+ std::mutex& outputLock;
+ List<CaptureResult>& resultQueue;
+ std::condition_variable& resultSignal;
+ uint32_t& nextShutterFrameNum;
+ uint32_t& nextReprocShutterFrameNum;
+ uint32_t& nextZslShutterFrameNum;
+ uint32_t& nextResultFrameNum;
+ uint32_t& nextReprocResultFrameNum;
+ uint32_t& nextZslResultFrameNum; // end of outputLock scope
+ const bool useHalBufManager;
+ const bool usePartialResult;
+ const bool needFixupMonoChrome;
+ const uint32_t numPartialResults;
+ const metadata_vendor_id_t vendorTagId;
+ const CameraMetadata& deviceInfo;
+ const std::unordered_map<std::string, CameraMetadata>& physicalDeviceInfoMap;
+ std::unique_ptr<ResultMetadataQueue>& fmq;
+ std::unordered_map<std::string, camera3::DistortionMapper>& distortionMappers;
+ std::unordered_map<std::string, camera3::ZoomRatioMapper>& zoomRatioMappers;
+ std::unordered_map<std::string, camera3::RotateAndCropMapper>& rotateAndCropMappers;
+ TagMonitor& tagMonitor;
+ sp<Camera3Stream> inputStream;
+ StreamSet& outputStreams;
+ sp<NotificationListener> listener;
+ SetErrorInterface& setErrIntf;
+ InflightRequestUpdateInterface& inflightIntf;
+ BufferRecordsInterface& bufferRecordsIntf;
+ };
+
+ // Handle one capture result. Assume callers hold the lock to serialize all
+ // processCaptureResult calls
+ void processOneCaptureResultLocked(
+ CaptureOutputStates& states,
+ const hardware::camera::device::V3_2::CaptureResult& result,
+ const hardware::hidl_vec<
+ hardware::camera::device::V3_4::PhysicalCameraMetadata> physicalCameraMetadata);
+
+ // Handle one notify message
+ void notify(CaptureOutputStates& states,
+ const hardware::camera::device::V3_2::NotifyMsg& msg);
+
+ struct RequestBufferStates {
+ const String8& cameraId;
+ std::mutex& reqBufferLock; // lock to serialize request buffer calls
+ const bool useHalBufManager;
+ StreamSet& outputStreams;
+ SetErrorInterface& setErrIntf;
+ BufferRecordsInterface& bufferRecordsIntf;
+ RequestBufferInterface& reqBufferIntf;
+ };
+
+ void requestStreamBuffers(RequestBufferStates& states,
+ const hardware::hidl_vec<hardware::camera::device::V3_5::BufferRequest>& bufReqs,
+ hardware::camera::device::V3_5::ICameraDeviceCallback::requestStreamBuffers_cb _hidl_cb);
+
+ struct ReturnBufferStates {
+ const String8& cameraId;
+ const bool useHalBufManager;
+ StreamSet& outputStreams;
+ BufferRecordsInterface& bufferRecordsIntf;
+ };
+
+ void returnStreamBuffers(ReturnBufferStates& states,
+ const hardware::hidl_vec<hardware::camera::device::V3_2::StreamBuffer>& buffers);
+
+ struct FlushInflightReqStates {
+ const String8& cameraId;
+ std::mutex& inflightLock;
+ InFlightRequestMap& inflightMap; // end of inflightLock scope
+ const bool useHalBufManager;
+ sp<NotificationListener> listener;
+ InflightRequestUpdateInterface& inflightIntf;
+ BufferRecordsInterface& bufferRecordsIntf;
+ FlushBufferInterface& flushBufferIntf;
+ };
+
+ void flushInflightRequests(FlushInflightReqStates& states);
+} // namespace camera3
+
+} // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
index b5e37c2..e645e05 100644
--- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
@@ -65,6 +65,12 @@
const std::vector<size_t> &removedSurfaceIds,
KeyedVector<sp<Surface>, size_t> *outputMap/*out*/);
+ virtual bool getOfflineProcessingSupport() const {
+ // As per Camera spec. shared streams currently do not support
+ // offline mode.
+ return false;
+ }
+
private:
static const size_t kMaxOutputs = 4;
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index f707ef8..7916ddb 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -151,6 +151,14 @@
return mPhysicalCameraId;
}
+void Camera3Stream::setOfflineProcessingSupport(bool support) {
+ mSupportOfflineProcessing = support;
+}
+
+bool Camera3Stream::getOfflineProcessingSupport() const {
+ return mSupportOfflineProcessing;
+}
+
status_t Camera3Stream::forceToIdle() {
ATRACE_CALL();
Mutex::Autolock l(mLock);
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 805df82..d768d3d 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -167,6 +167,9 @@
android_dataspace getOriginalDataSpace() const;
const String8& physicalCameraId() const;
+ void setOfflineProcessingSupport(bool) override;
+ bool getOfflineProcessingSupport() const override;
+
camera3_stream* asHalStream() override {
return this;
}
@@ -592,6 +595,8 @@
String8 mPhysicalCameraId;
nsecs_t mLastTimestamp;
+
+ bool mSupportOfflineProcessing = false;
}; // class Camera3Stream
}; // namespace camera3
diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
index 73f501a..667e3bb 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
@@ -23,6 +23,7 @@
#include "Camera3StreamBufferListener.h"
#include "Camera3StreamBufferFreedListener.h"
+struct camera3_stream;
struct camera3_stream_buffer;
namespace android {
@@ -54,6 +55,7 @@
android_dataspace dataSpace;
uint64_t consumerUsage;
bool finalized = false;
+ bool supportsOffline = false;
OutputStreamInfo() :
width(-1), height(-1), format(-1), dataSpace(HAL_DATASPACE_UNKNOWN),
consumerUsage(0) {}
@@ -99,6 +101,12 @@
virtual android_dataspace getOriginalDataSpace() const = 0;
/**
+ * Offline processing
+ */
+ virtual void setOfflineProcessingSupport(bool support) = 0;
+ virtual bool getOfflineProcessingSupport() const = 0;
+
+ /**
* Get a HAL3 handle for the stream, without starting stream configuration.
*/
virtual camera3_stream* asHalStream() = 0;
diff --git a/services/camera/libcameraservice/device3/CoordinateMapper.cpp b/services/camera/libcameraservice/device3/CoordinateMapper.cpp
new file mode 100644
index 0000000..a0bbe54
--- /dev/null
+++ b/services/camera/libcameraservice/device3/CoordinateMapper.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <system/camera_metadata_tags.h>
+
+#include "device3/CoordinateMapper.h"
+
+namespace android {
+
+namespace camera3 {
+
+/**
+ * Metadata keys to correct when adjusting coordinates for distortion correction
+ * or for crop and rotate
+ */
+
+// Both capture request and result
+constexpr std::array<uint32_t, 3> CoordinateMapper::kMeteringRegionsToCorrect = {
+ ANDROID_CONTROL_AF_REGIONS,
+ ANDROID_CONTROL_AE_REGIONS,
+ ANDROID_CONTROL_AWB_REGIONS
+};
+
+// Both capture request and result, not applicable to crop and rotate
+constexpr std::array<uint32_t, 1> CoordinateMapper::kRectsToCorrect = {
+ ANDROID_SCALER_CROP_REGION,
+};
+
+// Only for capture result
+constexpr std::array<uint32_t, 2> CoordinateMapper::kResultPointsToCorrectNoClamp = {
+ ANDROID_STATISTICS_FACE_RECTANGLES, // Says rectangles, is really points
+ ANDROID_STATISTICS_FACE_LANDMARKS,
+};
+
+} // namespace camera3
+
+} // namespace android
diff --git a/services/camera/libcameraservice/device3/CoordinateMapper.h b/services/camera/libcameraservice/device3/CoordinateMapper.h
new file mode 100644
index 0000000..5164856
--- /dev/null
+++ b/services/camera/libcameraservice/device3/CoordinateMapper.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SERVERS_COORDINATEMAPPER_H
+#define ANDROID_SERVERS_COORDINATEMAPPER_H
+
+#include <array>
+
+namespace android {
+
+namespace camera3 {
+
+class CoordinateMapper {
+ // Right now only stores metadata tags containing 2D coordinates
+ // to be corrected.
+protected:
+ // Metadata key lists to correct
+
+ // Both capture request and result
+ static const std::array<uint32_t, 3> kMeteringRegionsToCorrect;
+
+ // Both capture request and result
+ static const std::array<uint32_t, 1> kRectsToCorrect;
+
+ // Only for capture results; don't clamp
+ static const std::array<uint32_t, 2> kResultPointsToCorrectNoClamp;
+}; // class CoordinateMapper
+
+} // namespace camera3
+
+} // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/device3/DistortionMapper.cpp b/services/camera/libcameraservice/device3/DistortionMapper.cpp
index ae7af8e..8132225 100644
--- a/services/camera/libcameraservice/device3/DistortionMapper.cpp
+++ b/services/camera/libcameraservice/device3/DistortionMapper.cpp
@@ -27,41 +27,14 @@
namespace camera3 {
-/**
- * Metadata keys to correct when adjusting coordinates for distortion correction
- */
-
-// Both capture request and result
-constexpr std::array<uint32_t, 3> DistortionMapper::kMeteringRegionsToCorrect = {
- ANDROID_CONTROL_AF_REGIONS,
- ANDROID_CONTROL_AE_REGIONS,
- ANDROID_CONTROL_AWB_REGIONS
-};
-
-// Only capture request
-constexpr std::array<uint32_t, 1> DistortionMapper::kRequestRectsToCorrect = {
- ANDROID_SCALER_CROP_REGION,
-};
-
-// Only for capture result
-constexpr std::array<uint32_t, 1> DistortionMapper::kResultRectsToCorrect = {
- ANDROID_SCALER_CROP_REGION,
-};
-
-// Only for capture result
-constexpr std::array<uint32_t, 2> DistortionMapper::kResultPointsToCorrectNoClamp = {
- ANDROID_STATISTICS_FACE_RECTANGLES, // Says rectangles, is really points
- ANDROID_STATISTICS_FACE_LANDMARKS,
-};
-
DistortionMapper::DistortionMapper() : mValidMapping(false), mValidGrids(false) {
}
-bool DistortionMapper::isDistortionSupported(const CameraMetadata &result) {
+bool DistortionMapper::isDistortionSupported(const CameraMetadata &deviceInfo) {
bool isDistortionCorrectionSupported = false;
camera_metadata_ro_entry_t distortionCorrectionModes =
- result.find(ANDROID_DISTORTION_CORRECTION_AVAILABLE_MODES);
+ deviceInfo.find(ANDROID_DISTORTION_CORRECTION_AVAILABLE_MODES);
for (size_t i = 0; i < distortionCorrectionModes.count; i++) {
if (distortionCorrectionModes.data.u8[i] !=
ANDROID_DISTORTION_CORRECTION_MODE_OFF) {
@@ -124,7 +97,7 @@
if (res != OK) return res;
}
}
- for (auto rect : kRequestRectsToCorrect) {
+ for (auto rect : kRectsToCorrect) {
e = request->find(rect);
res = mapCorrectedRectToRaw(e.data.i32, e.count / 4, /*clamp*/true);
if (res != OK) return res;
@@ -160,7 +133,7 @@
if (res != OK) return res;
}
}
- for (auto rect : kResultRectsToCorrect) {
+ for (auto rect : kRectsToCorrect) {
e = result->find(rect);
res = mapRawRectToCorrected(e.data.i32, e.count / 4, /*clamp*/true);
if (res != OK) return res;
@@ -390,7 +363,6 @@
return OK;
}
-
status_t DistortionMapper::mapCorrectedRectToRaw(int32_t *rects, int rectCount, bool clamp,
bool simple) const {
if (!mValidMapping) return INVALID_OPERATION;
diff --git a/services/camera/libcameraservice/device3/DistortionMapper.h b/services/camera/libcameraservice/device3/DistortionMapper.h
index 4c0a1a6..7dcb67b 100644
--- a/services/camera/libcameraservice/device3/DistortionMapper.h
+++ b/services/camera/libcameraservice/device3/DistortionMapper.h
@@ -22,6 +22,7 @@
#include <mutex>
#include "camera/CameraMetadata.h"
+#include "device3/CoordinateMapper.h"
namespace android {
@@ -31,10 +32,19 @@
* Utilities to transform between raw (distorted) and warped (corrected) coordinate systems
* for cameras that support geometric distortion
*/
-class DistortionMapper {
+class DistortionMapper : private CoordinateMapper {
public:
DistortionMapper();
+ DistortionMapper(const DistortionMapper& other) :
+ mValidMapping(other.mValidMapping), mValidGrids(other.mValidGrids),
+ mFx(other.mFx), mFy(other.mFy), mCx(other.mCx), mCy(other.mCy), mS(other.mS),
+ mInvFx(other.mInvFx), mInvFy(other.mInvFy), mK(other.mK),
+ mArrayWidth(other.mArrayWidth), mArrayHeight(other.mArrayHeight),
+ mActiveWidth(other.mActiveWidth), mActiveHeight(other.mActiveHeight),
+ mArrayDiffX(other.mArrayDiffX), mArrayDiffY(other.mArrayDiffY),
+ mCorrectedGrid(other.mCorrectedGrid), mDistortedGrid(other.mDistortedGrid) {}
+
/**
* Check whether distortion correction is supported by the camera HAL
*/
@@ -150,20 +160,6 @@
// Fuzziness for float inequality tests
constexpr static float kFloatFuzz = 1e-4;
- // Metadata key lists to correct
-
- // Both capture request and result
- static const std::array<uint32_t, 3> kMeteringRegionsToCorrect;
-
- // Only capture request
- static const std::array<uint32_t, 1> kRequestRectsToCorrect;
-
- // Only capture result
- static const std::array<uint32_t, 1> kResultRectsToCorrect;
-
- // Only for capture results; don't clamp
- static const std::array<uint32_t, 2> kResultPointsToCorrectNoClamp;
-
// Single implementation for various mapCorrectedToRaw methods
template<typename T>
status_t mapCorrectedToRawImpl(T* coordPairs, int coordCount, bool clamp, bool simple) const;
@@ -186,7 +182,7 @@
// pre-calculated inverses for speed
float mInvFx, mInvFy;
// radial/tangential distortion parameters
- float mK[5];
+ std::array<float, 5> mK;
// pre-correction active array dimensions
float mArrayWidth, mArrayHeight;
diff --git a/services/camera/libcameraservice/device3/InFlightRequest.h b/services/camera/libcameraservice/device3/InFlightRequest.h
new file mode 100644
index 0000000..424043b
--- /dev/null
+++ b/services/camera/libcameraservice/device3/InFlightRequest.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SERVERS_CAMERA3_INFLIGHT_REQUEST_H
+#define ANDROID_SERVERS_CAMERA3_INFLIGHT_REQUEST_H
+
+#include <set>
+
+#include <camera/CaptureResult.h>
+#include <camera/CameraMetadata.h>
+#include <utils/String8.h>
+#include <utils/Timers.h>
+
+#include "hardware/camera3.h"
+
+#include "common/CameraDeviceBase.h"
+
+namespace android {
+
+namespace camera3 {
+
+struct InFlightRequest {
+ // Set by notify() SHUTTER call.
+ nsecs_t shutterTimestamp;
+ // Set by process_capture_result().
+ nsecs_t sensorTimestamp;
+ int requestStatus;
+ // Set by process_capture_result call with valid metadata
+ bool haveResultMetadata;
+ // Decremented by calls to process_capture_result with valid output
+ // and input buffers
+ int numBuffersLeft;
+ CaptureResultExtras resultExtras;
+ // If this request has any input buffer
+ bool hasInputBuffer;
+
+ // The last metadata that framework receives from HAL and
+ // not yet send out because the shutter event hasn't arrived.
+ // It's added by process_capture_result and sent when framework
+ // receives the shutter event.
+ CameraMetadata pendingMetadata;
+
+ // The metadata of the partial results that framework receives from HAL so far
+ // and has sent out.
+ CameraMetadata collectedPartialResult;
+
+ // Buffers are added by process_capture_result when output buffers
+ // return from HAL but framework has not yet received the shutter
+ // event. They will be returned to the streams when framework receives
+ // the shutter event.
+ Vector<camera3_stream_buffer_t> pendingOutputBuffers;
+
+ // Whether this inflight request's shutter and result callback are to be
+ // called. The policy is that if the request is the last one in the constrained
+ // high speed recording request list, this flag will be true. If the request list
+ // is not for constrained high speed recording, this flag will also be true.
+ bool hasCallback;
+
+ // Maximum expected frame duration for this request.
+ // For manual captures, equal to the max of requested exposure time and frame duration
+ // For auto-exposure modes, equal to 1/(lower end of target FPS range)
+ nsecs_t maxExpectedDuration;
+
+ // Whether the result metadata for this request is to be skipped. The
+ // result metadata should be skipped in the case of
+ // REQUEST/RESULT error.
+ bool skipResultMetadata;
+
+ // The physical camera ids being requested.
+ std::set<String8> physicalCameraIds;
+
+ // Map of physicalCameraId <-> Metadata
+ std::vector<PhysicalCaptureResultInfo> physicalMetadatas;
+
+ // Indicates a still capture request.
+ bool stillCapture;
+
+ // Indicates a ZSL capture request
+ bool zslCapture;
+
+ // Indicates that ROTATE_AND_CROP was set to AUTO
+ bool rotateAndCropAuto;
+
+ // Requested camera ids (both logical and physical) with zoomRatio != 1.0f
+ std::set<std::string> cameraIdsWithZoom;
+
+ // What shared surfaces an output should go to
+ SurfaceMap outputSurfaces;
+
+ // TODO: dedupe
+ static const nsecs_t kDefaultExpectedDuration = 100000000; // 100 ms
+
+ // Default constructor needed by KeyedVector
+ InFlightRequest() :
+ shutterTimestamp(0),
+ sensorTimestamp(0),
+ requestStatus(OK),
+ haveResultMetadata(false),
+ numBuffersLeft(0),
+ hasInputBuffer(false),
+ hasCallback(true),
+ maxExpectedDuration(kDefaultExpectedDuration),
+ skipResultMetadata(false),
+ stillCapture(false),
+ zslCapture(false),
+ rotateAndCropAuto(false) {
+ }
+
+ InFlightRequest(int numBuffers, CaptureResultExtras extras, bool hasInput,
+ bool hasAppCallback, nsecs_t maxDuration,
+ const std::set<String8>& physicalCameraIdSet, bool isStillCapture,
+ bool isZslCapture, bool rotateAndCropAuto, const std::set<std::string>& idsWithZoom,
+ const SurfaceMap& outSurfaces = SurfaceMap{}) :
+ shutterTimestamp(0),
+ sensorTimestamp(0),
+ requestStatus(OK),
+ haveResultMetadata(false),
+ numBuffersLeft(numBuffers),
+ resultExtras(extras),
+ hasInputBuffer(hasInput),
+ hasCallback(hasAppCallback),
+ maxExpectedDuration(maxDuration),
+ skipResultMetadata(false),
+ physicalCameraIds(physicalCameraIdSet),
+ stillCapture(isStillCapture),
+ zslCapture(isZslCapture),
+ rotateAndCropAuto(rotateAndCropAuto),
+ cameraIdsWithZoom(idsWithZoom),
+ outputSurfaces(outSurfaces) {
+ }
+};
+
+// Map from frame number to the in-flight request state
+typedef KeyedVector<uint32_t, InFlightRequest> InFlightRequestMap;
+
+} // namespace camera3
+
+} // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/device3/RotateAndCropMapper.cpp b/services/camera/libcameraservice/device3/RotateAndCropMapper.cpp
new file mode 100644
index 0000000..3718f54
--- /dev/null
+++ b/services/camera/libcameraservice/device3/RotateAndCropMapper.cpp
@@ -0,0 +1,331 @@
+/*
+ * 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_TAG "Camera3-RotCropMapper"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include <algorithm>
+#include <cmath>
+
+#include "device3/RotateAndCropMapper.h"
+
+namespace android {
+
+namespace camera3 {
+
+bool RotateAndCropMapper::isNeeded(const CameraMetadata* deviceInfo) {
+ auto entry = deviceInfo->find(ANDROID_SCALER_AVAILABLE_ROTATE_AND_CROP_MODES);
+ for (size_t i = 0; i < entry.count; i++) {
+ if (entry.data.u8[i] == ANDROID_SCALER_ROTATE_AND_CROP_AUTO) return true;
+ }
+ return false;
+}
+
+RotateAndCropMapper::RotateAndCropMapper(const CameraMetadata* deviceInfo) {
+ auto entry = deviceInfo->find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+ if (entry.count != 4) return;
+
+ mArrayWidth = entry.data.i32[2];
+ mArrayHeight = entry.data.i32[3];
+ mArrayAspect = static_cast<float>(mArrayWidth) / mArrayHeight;
+ mRotateAspect = 1.f/mArrayAspect;
+}
+
+/**
+ * Adjust capture request when rotate and crop AUTO is enabled
+ */
+status_t RotateAndCropMapper::updateCaptureRequest(CameraMetadata *request) {
+ auto entry = request->find(ANDROID_SCALER_ROTATE_AND_CROP);
+ if (entry.count == 0) return OK;
+ uint8_t rotateMode = entry.data.u8[0];
+ if (rotateMode == ANDROID_SCALER_ROTATE_AND_CROP_NONE) return OK;
+
+ int32_t cx = 0;
+ int32_t cy = 0;
+ int32_t cw = mArrayWidth;
+ int32_t ch = mArrayHeight;
+ entry = request->find(ANDROID_SCALER_CROP_REGION);
+ if (entry.count == 4) {
+ cx = entry.data.i32[0];
+ cy = entry.data.i32[1];
+ cw = entry.data.i32[2];
+ ch = entry.data.i32[3];
+ }
+
+ // User inputs are relative to the rotated-and-cropped view, so convert back
+ // to active array coordinates. To be more specific, the application is
+ // calculating coordinates based on the crop rectangle and the active array,
+ // even though the view the user sees is the cropped-and-rotated one. So we
+ // need to adjust the coordinates so that a point that would be on the
+ // top-left corner of the crop region is mapped to the top-left corner of
+ // the rotated-and-cropped fov within the crop region, and the same for the
+ // bottom-right corner.
+ //
+ // Since the zoom ratio control scales everything uniformly (so an app does
+ // not need to adjust anything if it wants to put a metering region on the
+ // top-left quadrant of the preview FOV, when changing zoomRatio), it does
+ // not need to be factored into this calculation at all.
+ //
+ // ->+x active array aw
+ // |+--------------------------------------------------------------------+
+ // v| |
+ // +y| a 1 cw 2 b |
+ // | +=========*HHHHHHHHHHHHHHH*===========+ |
+ // | I H rw H I |
+ // | I H H I |
+ // | I H H I |
+ //ah | ch I H rh H I crop region |
+ // | I H H I |
+ // | I H H I |
+ // | I H rotate region H I |
+ // | +=========*HHHHHHHHHHHHHHH*===========+ |
+ // | d 4 3 c |
+ // | |
+ // +--------------------------------------------------------------------+
+ //
+ // aw , ah = active array width,height
+ // cw , ch = crop region width,height
+ // rw , rh = rotated-and-cropped region width,height
+ // aw / ah = array aspect = rh / rw = 1 / rotated aspect
+ // Coordinate mappings:
+ // ROTATE_AND_CROP_90: point a -> point 2
+ // point c -> point 4 = +x -> +y, +y -> -x
+ // ROTATE_AND_CROP_180: point a -> point c
+ // point c -> point a = +x -> -x, +y -> -y
+ // ROTATE_AND_CROP_270: point a -> point 4
+ // point c -> point 2 = +x -> -y, +y -> +x
+
+ float cropAspect = static_cast<float>(cw) / ch;
+ float transformMat[4] = {0, 0,
+ 0, 0};
+ float xShift = 0;
+ float yShift = 0;
+
+ if (rotateMode == ANDROID_SCALER_ROTATE_AND_CROP_180) {
+ transformMat[0] = -1;
+ transformMat[3] = -1;
+ xShift = cw;
+ yShift = ch;
+ } else {
+ float rw = cropAspect > mRotateAspect ?
+ ch * mRotateAspect : // pillarbox, not full width
+ cw; // letterbox or 1:1, full width
+ float rh = cropAspect >= mRotateAspect ?
+ ch : // pillarbox or 1:1, full height
+ cw / mRotateAspect; // letterbox, not full height
+ switch (rotateMode) {
+ case ANDROID_SCALER_ROTATE_AND_CROP_90:
+ transformMat[1] = -rw / ch; // +y -> -x
+ transformMat[2] = rh / cw; // +x -> +y
+ xShift = (cw + rw) / 2; // left edge of crop to right edge of rotated
+ yShift = (ch - rh) / 2; // top edge of crop to top edge of rotated
+ break;
+ case ANDROID_SCALER_ROTATE_AND_CROP_270:
+ transformMat[1] = rw / ch; // +y -> +x
+ transformMat[2] = -rh / cw; // +x -> -y
+ xShift = (cw - rw) / 2; // left edge of crop to left edge of rotated
+ yShift = (ch + rh) / 2; // top edge of crop to bottom edge of rotated
+ break;
+ default:
+ ALOGE("%s: Unexpected rotate mode: %d", __FUNCTION__, rotateMode);
+ return BAD_VALUE;
+ }
+ }
+
+ for (auto regionTag : kMeteringRegionsToCorrect) {
+ entry = request->find(regionTag);
+ for (size_t i = 0; i < entry.count; i += 5) {
+ int32_t weight = entry.data.i32[i + 4];
+ if (weight == 0) {
+ continue;
+ }
+ transformPoints(entry.data.i32 + i, 2, transformMat, xShift, yShift, cx, cy);
+ swapRectToMinFirst(entry.data.i32 + i);
+ }
+ }
+
+ return OK;
+}
+
+/**
+ * Adjust capture result when rotate and crop AUTO is enabled
+ */
+status_t RotateAndCropMapper::updateCaptureResult(CameraMetadata *result) {
+ auto entry = result->find(ANDROID_SCALER_ROTATE_AND_CROP);
+ if (entry.count == 0) return OK;
+ uint8_t rotateMode = entry.data.u8[0];
+ if (rotateMode == ANDROID_SCALER_ROTATE_AND_CROP_NONE) return OK;
+
+ int32_t cx = 0;
+ int32_t cy = 0;
+ int32_t cw = mArrayWidth;
+ int32_t ch = mArrayHeight;
+ entry = result->find(ANDROID_SCALER_CROP_REGION);
+ if (entry.count == 4) {
+ cx = entry.data.i32[0];
+ cy = entry.data.i32[1];
+ cw = entry.data.i32[2];
+ ch = entry.data.i32[3];
+ }
+
+ // HAL inputs are relative to the full active array, so convert back to
+ // rotated-and-cropped coordinates for apps. To be more specific, the
+ // application is calculating coordinates based on the crop rectangle and
+ // the active array, even though the view the user sees is the
+ // cropped-and-rotated one. So we need to adjust the coordinates so that a
+ // point that would be on the top-left corner of the rotate-and-cropped
+ // region is mapped to the top-left corner of the crop region, and the same
+ // for the bottom-right corner.
+ //
+ // Since the zoom ratio control scales everything uniformly (so an app does
+ // not need to adjust anything if it wants to put a metering region on the
+ // top-left quadrant of the preview FOV, when changing zoomRatio), it does
+ // not need to be factored into this calculation at all.
+ //
+ // Also note that round-tripping between original request and final result
+ // fields can't be perfect, since the intermediate values have to be
+ // integers on a smaller range than the original crop region range. That
+ // means that multiple input values map to a single output value in
+ // adjusting a request, so when adjusting a result, the original answer may
+ // not be obtainable. Given that aspect ratios are rarely > 16/9, the
+ // round-trip values should generally only be off by 1 at most.
+ //
+ // ->+x active array aw
+ // |+--------------------------------------------------------------------+
+ // v| |
+ // +y| a 1 cw 2 b |
+ // | +=========*HHHHHHHHHHHHHHH*===========+ |
+ // | I H rw H I |
+ // | I H H I |
+ // | I H H I |
+ //ah | ch I H rh H I crop region |
+ // | I H H I |
+ // | I H H I |
+ // | I H rotate region H I |
+ // | +=========*HHHHHHHHHHHHHHH*===========+ |
+ // | d 4 3 c |
+ // | |
+ // +--------------------------------------------------------------------+
+ //
+ // aw , ah = active array width,height
+ // cw , ch = crop region width,height
+ // rw , rh = rotated-and-cropped region width,height
+ // aw / ah = array aspect = rh / rw = 1 / rotated aspect
+ // Coordinate mappings:
+ // ROTATE_AND_CROP_90: point 2 -> point a
+ // point 4 -> point c = +x -> -y, +y -> +x
+ // ROTATE_AND_CROP_180: point c -> point a
+ // point a -> point c = +x -> -x, +y -> -y
+ // ROTATE_AND_CROP_270: point 4 -> point a
+ // point 2 -> point c = +x -> +y, +y -> -x
+
+ float cropAspect = static_cast<float>(cw) / ch;
+ float transformMat[4] = {0, 0,
+ 0, 0};
+ float xShift = 0;
+ float yShift = 0;
+ float rx = 0; // top-left corner of rotated region
+ float ry = 0;
+ if (rotateMode == ANDROID_SCALER_ROTATE_AND_CROP_180) {
+ transformMat[0] = -1;
+ transformMat[3] = -1;
+ xShift = cw;
+ yShift = ch;
+ rx = cx;
+ ry = cy;
+ } else {
+ float rw = cropAspect > mRotateAspect ?
+ ch * mRotateAspect : // pillarbox, not full width
+ cw; // letterbox or 1:1, full width
+ float rh = cropAspect >= mRotateAspect ?
+ ch : // pillarbox or 1:1, full height
+ cw / mRotateAspect; // letterbox, not full height
+ rx = cx + (cw - rw) / 2;
+ ry = cy + (ch - rh) / 2;
+ switch (rotateMode) {
+ case ANDROID_SCALER_ROTATE_AND_CROP_90:
+ transformMat[1] = ch / rw; // +y -> +x
+ transformMat[2] = -cw / rh; // +x -> -y
+ xShift = -(cw - rw) / 2; // left edge of rotated to left edge of cropped
+ yShift = ry - cy + ch; // top edge of rotated to bottom edge of cropped
+ break;
+ case ANDROID_SCALER_ROTATE_AND_CROP_270:
+ transformMat[1] = -ch / rw; // +y -> -x
+ transformMat[2] = cw / rh; // +x -> +y
+ xShift = (cw + rw) / 2; // left edge of rotated to left edge of cropped
+ yShift = (ch - rh) / 2; // top edge of rotated to bottom edge of cropped
+ break;
+ default:
+ ALOGE("%s: Unexpected rotate mode: %d", __FUNCTION__, rotateMode);
+ return BAD_VALUE;
+ }
+ }
+
+ for (auto regionTag : kMeteringRegionsToCorrect) {
+ entry = result->find(regionTag);
+ for (size_t i = 0; i < entry.count; i += 5) {
+ int32_t weight = entry.data.i32[i + 4];
+ if (weight == 0) {
+ continue;
+ }
+ transformPoints(entry.data.i32 + i, 2, transformMat, xShift, yShift, rx, ry);
+ swapRectToMinFirst(entry.data.i32 + i);
+ }
+ }
+
+ for (auto pointsTag: kResultPointsToCorrectNoClamp) {
+ entry = result->find(pointsTag);
+ transformPoints(entry.data.i32, entry.count / 2, transformMat, xShift, yShift, rx, ry);
+ if (pointsTag == ANDROID_STATISTICS_FACE_RECTANGLES) {
+ for (size_t i = 0; i < entry.count; i += 4) {
+ swapRectToMinFirst(entry.data.i32 + i);
+ }
+ }
+ }
+
+ return OK;
+}
+
+void RotateAndCropMapper::transformPoints(int32_t* pts, size_t count, float transformMat[4],
+ float xShift, float yShift, float ox, float oy) {
+ for (size_t i = 0; i < count * 2; i += 2) {
+ float x0 = pts[i] - ox;
+ float y0 = pts[i + 1] - oy;
+ int32_t nx = std::round(transformMat[0] * x0 + transformMat[1] * y0 + xShift + ox);
+ int32_t ny = std::round(transformMat[2] * x0 + transformMat[3] * y0 + yShift + oy);
+
+ pts[i] = std::min(std::max(nx, 0), mArrayWidth);
+ pts[i + 1] = std::min(std::max(ny, 0), mArrayHeight);
+ }
+}
+
+void RotateAndCropMapper::swapRectToMinFirst(int32_t* rect) {
+ if (rect[0] > rect[2]) {
+ auto tmp = rect[0];
+ rect[0] = rect[2];
+ rect[2] = tmp;
+ }
+ if (rect[1] > rect[3]) {
+ auto tmp = rect[1];
+ rect[1] = rect[3];
+ rect[3] = tmp;
+ }
+}
+
+} // namespace camera3
+
+} // namespace android
diff --git a/services/camera/libcameraservice/device3/RotateAndCropMapper.h b/services/camera/libcameraservice/device3/RotateAndCropMapper.h
new file mode 100644
index 0000000..459e27f
--- /dev/null
+++ b/services/camera/libcameraservice/device3/RotateAndCropMapper.h
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_SERVERS_ROTATEANDCROPMAPPER_H
+#define ANDROID_SERVERS_ROTATEANDCROPMAPPER_H
+
+#include <utils/Errors.h>
+#include <array>
+#include <mutex>
+
+#include "camera/CameraMetadata.h"
+#include "device3/CoordinateMapper.h"
+
+namespace android {
+
+namespace camera3 {
+
+/**
+ * Utilities to transform between unrotated and rotated-and-cropped coordinate systems
+ * for cameras that support SCALER_ROTATE_AND_CROP controls in AUTO mode.
+ */
+class RotateAndCropMapper : private CoordinateMapper {
+ public:
+ static bool isNeeded(const CameraMetadata* deviceInfo);
+
+ RotateAndCropMapper(const CameraMetadata* deviceInfo);
+
+ /**
+ * Adjust capture request assuming rotate and crop AUTO is enabled
+ */
+ status_t updateCaptureRequest(CameraMetadata *request);
+
+ /**
+ * Adjust capture result assuming rotate and crop AUTO is enabled
+ */
+ status_t updateCaptureResult(CameraMetadata *result);
+
+ private:
+ // Transform count's worth of x,y points passed in with 2x2 matrix + translate with transform
+ // origin (cx,cy)
+ void transformPoints(int32_t* pts, size_t count, float transformMat[4],
+ float xShift, float yShift, float cx, float cy);
+ // Take two corners of a rect as (x1,y1,x2,y2) and swap x and y components
+ // if needed so that x1 < x2, y1 < y2.
+ void swapRectToMinFirst(int32_t* rect);
+
+ int32_t mArrayWidth, mArrayHeight;
+ float mArrayAspect, mRotateAspect;
+}; // class RotateAndCroMapper
+
+} // namespace camera3
+
+} // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/device3/ZoomRatioMapper.cpp b/services/camera/libcameraservice/device3/ZoomRatioMapper.cpp
new file mode 100644
index 0000000..84da45a
--- /dev/null
+++ b/services/camera/libcameraservice/device3/ZoomRatioMapper.cpp
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Camera3-ZoomRatioMapper"
+//#define LOG_NDEBUG 0
+
+#include <algorithm>
+
+#include "device3/ZoomRatioMapper.h"
+
+namespace android {
+
+namespace camera3 {
+
+
+status_t ZoomRatioMapper::initZoomRatioInTemplate(CameraMetadata *request) {
+ camera_metadata_entry_t entry;
+ entry = request->find(ANDROID_CONTROL_ZOOM_RATIO);
+ float defaultZoomRatio = 1.0f;
+ if (entry.count == 0) {
+ return request->update(ANDROID_CONTROL_ZOOM_RATIO, &defaultZoomRatio, 1);
+ }
+ return OK;
+}
+
+status_t ZoomRatioMapper::overrideZoomRatioTags(
+ CameraMetadata* deviceInfo, bool* supportNativeZoomRatio) {
+ if (deviceInfo == nullptr || supportNativeZoomRatio == nullptr) {
+ return BAD_VALUE;
+ }
+
+ camera_metadata_entry_t entry;
+ entry = deviceInfo->find(ANDROID_CONTROL_ZOOM_RATIO_RANGE);
+ if (entry.count != 2 && entry.count != 0) return BAD_VALUE;
+
+ // Hal has zoom ratio support
+ if (entry.count == 2) {
+ *supportNativeZoomRatio = true;
+ return OK;
+ }
+
+ // Hal has no zoom ratio support
+ *supportNativeZoomRatio = false;
+
+ entry = deviceInfo->find(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM);
+ if (entry.count != 1) {
+ ALOGI("%s: Camera device doesn't support SCALER_AVAILABLE_MAX_DIGITAL_ZOOM key!",
+ __FUNCTION__);
+ return OK;
+ }
+
+ float zoomRange[] = {1.0f, entry.data.f[0]};
+ status_t res = deviceInfo->update(ANDROID_CONTROL_ZOOM_RATIO_RANGE, zoomRange, 2);
+ if (res != OK) {
+ ALOGE("%s: Failed to update CONTROL_ZOOM_RATIO_RANGE key: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+
+ std::vector<int32_t> requestKeys;
+ entry = deviceInfo->find(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS);
+ if (entry.count > 0) {
+ requestKeys.insert(requestKeys.end(), entry.data.i32, entry.data.i32 + entry.count);
+ }
+ requestKeys.push_back(ANDROID_CONTROL_ZOOM_RATIO);
+ res = deviceInfo->update(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS,
+ requestKeys.data(), requestKeys.size());
+ if (res != OK) {
+ ALOGE("%s: Failed to update REQUEST_AVAILABLE_REQUEST_KEYS: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+
+ std::vector<int32_t> resultKeys;
+ entry = deviceInfo->find(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS);
+ if (entry.count > 0) {
+ resultKeys.insert(resultKeys.end(), entry.data.i32, entry.data.i32 + entry.count);
+ }
+ resultKeys.push_back(ANDROID_CONTROL_ZOOM_RATIO);
+ res = deviceInfo->update(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS,
+ resultKeys.data(), resultKeys.size());
+ if (res != OK) {
+ ALOGE("%s: Failed to update REQUEST_AVAILABLE_RESULT_KEYS: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+
+ std::vector<int32_t> charKeys;
+ entry = deviceInfo->find(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS);
+ if (entry.count > 0) {
+ charKeys.insert(charKeys.end(), entry.data.i32, entry.data.i32 + entry.count);
+ }
+ charKeys.push_back(ANDROID_CONTROL_ZOOM_RATIO_RANGE);
+ res = deviceInfo->update(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
+ charKeys.data(), charKeys.size());
+ if (res != OK) {
+ ALOGE("%s: Failed to update REQUEST_AVAILABLE_CHARACTERISTICS_KEYS: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+
+ return OK;
+}
+
+ZoomRatioMapper::ZoomRatioMapper(const CameraMetadata* deviceInfo,
+ bool supportNativeZoomRatio, bool usePrecorrectArray) {
+ camera_metadata_ro_entry_t entry;
+
+ entry = deviceInfo->find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
+ if (entry.count != 4) return;
+ int32_t arrayW = entry.data.i32[2];
+ int32_t arrayH = entry.data.i32[3];
+
+ entry = deviceInfo->find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+ if (entry.count != 4) return;
+ int32_t activeW = entry.data.i32[2];
+ int32_t activeH = entry.data.i32[3];
+
+ if (usePrecorrectArray) {
+ mArrayWidth = arrayW;
+ mArrayHeight = arrayH;
+ } else {
+ mArrayWidth = activeW;
+ mArrayHeight = activeH;
+ }
+ mHalSupportsZoomRatio = supportNativeZoomRatio;
+
+ ALOGV("%s: array size: %d x %d, mHalSupportsZoomRatio %d",
+ __FUNCTION__, mArrayWidth, mArrayHeight, mHalSupportsZoomRatio);
+ mIsValid = true;
+}
+
+status_t ZoomRatioMapper::updateCaptureRequest(CameraMetadata* request) {
+ if (!mIsValid) return INVALID_OPERATION;
+
+ status_t res = OK;
+ bool zoomRatioIs1 = true;
+ camera_metadata_entry_t entry;
+
+ entry = request->find(ANDROID_CONTROL_ZOOM_RATIO);
+ if (entry.count == 1 && entry.data.f[0] != 1.0f) {
+ zoomRatioIs1 = false;
+ }
+
+ if (mHalSupportsZoomRatio && zoomRatioIs1) {
+ res = separateZoomFromCropLocked(request, false/*isResult*/);
+ } else if (!mHalSupportsZoomRatio && !zoomRatioIs1) {
+ res = combineZoomAndCropLocked(request, false/*isResult*/);
+ }
+
+ // If CONTROL_ZOOM_RATIO is in request, but HAL doesn't support
+ // CONTROL_ZOOM_RATIO, remove it from the request.
+ if (!mHalSupportsZoomRatio && entry.count == 1) {
+ request->erase(ANDROID_CONTROL_ZOOM_RATIO);
+ }
+
+ return res;
+}
+
+status_t ZoomRatioMapper::updateCaptureResult(CameraMetadata* result, bool requestedZoomRatioIs1) {
+ if (!mIsValid) return INVALID_OPERATION;
+
+ status_t res = OK;
+
+ if (mHalSupportsZoomRatio && requestedZoomRatioIs1) {
+ res = combineZoomAndCropLocked(result, true/*isResult*/);
+ } else if (!mHalSupportsZoomRatio && !requestedZoomRatioIs1) {
+ res = separateZoomFromCropLocked(result, true/*isResult*/);
+ } else {
+ camera_metadata_entry_t entry = result->find(ANDROID_CONTROL_ZOOM_RATIO);
+ if (entry.count == 0) {
+ float zoomRatio1x = 1.0f;
+ result->update(ANDROID_CONTROL_ZOOM_RATIO, &zoomRatio1x, 1);
+ }
+ }
+
+ return res;
+}
+
+float ZoomRatioMapper::deriveZoomRatio(const CameraMetadata* metadata) {
+ float zoomRatio = 1.0;
+
+ camera_metadata_ro_entry_t entry;
+ entry = metadata->find(ANDROID_SCALER_CROP_REGION);
+ if (entry.count != 4) return zoomRatio;
+
+ // Center of the preCorrection/active size
+ float arrayCenterX = mArrayWidth / 2.0;
+ float arrayCenterY = mArrayHeight / 2.0;
+
+ // Re-map crop region to coordinate system centered to (arrayCenterX,
+ // arrayCenterY).
+ float cropRegionLeft = arrayCenterX - entry.data.i32[0] ;
+ float cropRegionTop = arrayCenterY - entry.data.i32[1];
+ float cropRegionRight = entry.data.i32[0] + entry.data.i32[2] - arrayCenterX;
+ float cropRegionBottom = entry.data.i32[1] + entry.data.i32[3] - arrayCenterY;
+
+ // Calculate the scaling factor for left, top, bottom, right
+ float zoomRatioLeft = std::max(mArrayWidth / (2 * cropRegionLeft), 1.0f);
+ float zoomRatioTop = std::max(mArrayHeight / (2 * cropRegionTop), 1.0f);
+ float zoomRatioRight = std::max(mArrayWidth / (2 * cropRegionRight), 1.0f);
+ float zoomRatioBottom = std::max(mArrayHeight / (2 * cropRegionBottom), 1.0f);
+
+ // Use minimum scaling factor to handle letterboxing or pillarboxing
+ zoomRatio = std::min(std::min(zoomRatioLeft, zoomRatioRight),
+ std::min(zoomRatioTop, zoomRatioBottom));
+
+ ALOGV("%s: derived zoomRatio is %f", __FUNCTION__, zoomRatio);
+ return zoomRatio;
+}
+
+status_t ZoomRatioMapper::separateZoomFromCropLocked(CameraMetadata* metadata, bool isResult) {
+ status_t res;
+ float zoomRatio = deriveZoomRatio(metadata);
+
+ // Update zoomRatio metadata tag
+ res = metadata->update(ANDROID_CONTROL_ZOOM_RATIO, &zoomRatio, 1);
+ if (res != OK) {
+ ALOGE("%s: Failed to update ANDROID_CONTROL_ZOOM_RATIO: %s(%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+
+ // Scale regions using zoomRatio
+ camera_metadata_entry_t entry;
+ for (auto region : kMeteringRegionsToCorrect) {
+ entry = metadata->find(region);
+ for (size_t j = 0; j < entry.count; j += 5) {
+ int32_t weight = entry.data.i32[j + 4];
+ if (weight == 0) {
+ continue;
+ }
+ // Top-left is inclusively clamped
+ scaleCoordinates(entry.data.i32 + j, 1, zoomRatio, ClampInclusive);
+ // Bottom-right is exclusively clamped
+ scaleCoordinates(entry.data.i32 + j + 2, 1, zoomRatio, ClampExclusive);
+ }
+ }
+
+ for (auto rect : kRectsToCorrect) {
+ entry = metadata->find(rect);
+ scaleRects(entry.data.i32, entry.count / 4, zoomRatio);
+ }
+
+ if (isResult) {
+ for (auto pts : kResultPointsToCorrectNoClamp) {
+ entry = metadata->find(pts);
+ scaleCoordinates(entry.data.i32, entry.count / 2, zoomRatio, ClampOff);
+ }
+ }
+
+ return OK;
+}
+
+status_t ZoomRatioMapper::combineZoomAndCropLocked(CameraMetadata* metadata, bool isResult) {
+ float zoomRatio = 1.0f;
+ camera_metadata_entry_t entry;
+ entry = metadata->find(ANDROID_CONTROL_ZOOM_RATIO);
+ if (entry.count == 1) {
+ zoomRatio = entry.data.f[0];
+ }
+
+ // Unscale regions with zoomRatio
+ status_t res;
+ for (auto region : kMeteringRegionsToCorrect) {
+ entry = metadata->find(region);
+ for (size_t j = 0; j < entry.count; j += 5) {
+ int32_t weight = entry.data.i32[j + 4];
+ if (weight == 0) {
+ continue;
+ }
+ // Top-left is inclusively clamped
+ scaleCoordinates(entry.data.i32 + j, 1, 1.0 / zoomRatio, ClampInclusive);
+ // Bottom-right is exclusively clamped
+ scaleCoordinates(entry.data.i32 + j + 2, 1, 1.0 / zoomRatio, ClampExclusive);
+ }
+ }
+ for (auto rect : kRectsToCorrect) {
+ entry = metadata->find(rect);
+ scaleRects(entry.data.i32, entry.count / 4, 1.0 / zoomRatio);
+ }
+ if (isResult) {
+ for (auto pts : kResultPointsToCorrectNoClamp) {
+ entry = metadata->find(pts);
+ scaleCoordinates(entry.data.i32, entry.count / 2, 1.0 / zoomRatio, ClampOff);
+ }
+ }
+
+ zoomRatio = 1.0;
+ res = metadata->update(ANDROID_CONTROL_ZOOM_RATIO, &zoomRatio, 1);
+ if (res != OK) {
+ return res;
+ }
+
+ return OK;
+}
+
+void ZoomRatioMapper::scaleCoordinates(int32_t* coordPairs, int coordCount,
+ float scaleRatio, ClampMode clamp) {
+ for (int i = 0; i < coordCount * 2; i += 2) {
+ float x = coordPairs[i];
+ float y = coordPairs[i + 1];
+ float xCentered = x - mArrayWidth / 2;
+ float yCentered = y - mArrayHeight / 2;
+ float scaledX = xCentered * scaleRatio;
+ float scaledY = yCentered * scaleRatio;
+ scaledX += mArrayWidth / 2;
+ scaledY += mArrayHeight / 2;
+ // Clamp to within activeArray/preCorrectionActiveArray
+ coordPairs[i] = static_cast<int32_t>(scaledX);
+ coordPairs[i+1] = static_cast<int32_t>(scaledY);
+ if (clamp != ClampOff) {
+ int32_t right, bottom;
+ if (clamp == ClampInclusive) {
+ right = mArrayWidth - 1;
+ bottom = mArrayHeight - 1;
+ } else {
+ right = mArrayWidth;
+ bottom = mArrayHeight;
+ }
+ coordPairs[i] =
+ std::min(right, std::max(0, coordPairs[i]));
+ coordPairs[i+1] =
+ std::min(bottom, std::max(0, coordPairs[i+1]));
+ }
+ ALOGV("%s: coordinates: %d, %d", __FUNCTION__, coordPairs[i], coordPairs[i+1]);
+ }
+}
+
+void ZoomRatioMapper::scaleRects(int32_t* rects, int rectCount,
+ float scaleRatio) {
+ for (int i = 0; i < rectCount * 4; i += 4) {
+ // Map from (l, t, width, height) to (l, t, r, b).
+ // [l, t] is inclusive, and [r, b] is exclusive.
+ int32_t coords[4] = {
+ rects[i],
+ rects[i + 1],
+ rects[i] + rects[i + 2],
+ rects[i + 1] + rects[i + 3]
+ };
+
+ // top-left
+ scaleCoordinates(coords, 1, scaleRatio, ClampInclusive);
+ // bottom-right
+ scaleCoordinates(coords+2, 1, scaleRatio, ClampExclusive);
+
+ // Map back to (l, t, width, height)
+ rects[i] = coords[0];
+ rects[i + 1] = coords[1];
+ rects[i + 2] = coords[2] - coords[0];
+ rects[i + 3] = coords[3] - coords[1];
+ }
+}
+
+} // namespace camera3
+
+} // namespace android
diff --git a/services/camera/libcameraservice/device3/ZoomRatioMapper.h b/services/camera/libcameraservice/device3/ZoomRatioMapper.h
new file mode 100644
index 0000000..aa3d913
--- /dev/null
+++ b/services/camera/libcameraservice/device3/ZoomRatioMapper.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SERVERS_ZOOMRATIOMAPPER_H
+#define ANDROID_SERVERS_ZOOMRATIOMAPPER_H
+
+#include <utils/Errors.h>
+#include <array>
+
+#include "camera/CameraMetadata.h"
+#include "device3/CoordinateMapper.h"
+
+namespace android {
+
+namespace camera3 {
+
+/**
+ * Utilities to convert between the new zoomRatio and existing cropRegion
+ * metadata tags. Note that this class does conversions in 2 scenarios:
+ * - HAL supports zoomRatio and the application uses cropRegion, or
+ * - HAL doesn't support zoomRatio, but the application uses zoomRatio
+ */
+class ZoomRatioMapper : private CoordinateMapper {
+ public:
+ ZoomRatioMapper() = default;
+ ZoomRatioMapper(const CameraMetadata *deviceInfo,
+ bool supportNativeZoomRatio, bool usePrecorrectArray);
+ ZoomRatioMapper(const ZoomRatioMapper& other) :
+ mHalSupportsZoomRatio(other.mHalSupportsZoomRatio),
+ mArrayWidth(other.mArrayWidth), mArrayHeight(other.mArrayHeight),
+ mIsValid(other.mIsValid) {}
+
+ /**
+ * Initialize request template with valid zoomRatio if necessary.
+ */
+ static status_t initZoomRatioInTemplate(CameraMetadata *request);
+
+ /**
+ * Override zoomRatio related tags in the static metadata.
+ */
+ static status_t overrideZoomRatioTags(
+ CameraMetadata* deviceInfo, bool* supportNativeZoomRatio);
+
+ /**
+ * Update capture request to handle both cropRegion and zoomRatio.
+ */
+ status_t updateCaptureRequest(CameraMetadata *request);
+
+ /**
+ * Update capture result to handle both cropRegion and zoomRatio.
+ */
+ status_t updateCaptureResult(CameraMetadata *request, bool requestedZoomRatioIs1);
+
+ public: // Visible for testing. Do not use concurently.
+ enum ClampMode {
+ ClampOff,
+ ClampInclusive,
+ ClampExclusive,
+ };
+
+ void scaleCoordinates(int32_t* coordPairs, int coordCount,
+ float scaleRatio, ClampMode clamp);
+
+ bool isValid() { return mIsValid; }
+ private:
+ // const after construction
+ bool mHalSupportsZoomRatio;
+ // active array / pre-correction array dimension
+ int32_t mArrayWidth, mArrayHeight;
+
+ bool mIsValid = false;
+
+ float deriveZoomRatio(const CameraMetadata* metadata);
+ void scaleRects(int32_t* rects, int rectCount, float scaleRatio);
+
+ status_t separateZoomFromCropLocked(CameraMetadata* metadata, bool isResult);
+ status_t combineZoomAndCropLocked(CameraMetadata* metadata, bool isResult);
+};
+
+} // namespace camera3
+
+} // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/hidl/AidlCameraServiceListener.h b/services/camera/libcameraservice/hidl/AidlCameraServiceListener.h
index 0f6be79..175eb8e 100644
--- a/services/camera/libcameraservice/hidl/AidlCameraServiceListener.h
+++ b/services/camera/libcameraservice/hidl/AidlCameraServiceListener.h
@@ -46,12 +46,18 @@
~H2BCameraServiceListener() { }
virtual ::android::binder::Status onStatusChanged(int32_t status,
- const ::android::String16& cameraId) override;
+ const ::android::String16& cameraId) override;
+ virtual ::android::binder::Status onPhysicalCameraStatusChanged(int32_t /*status*/,
+ const ::android::String16& /*cameraId*/,
+ const ::android::String16& /*physicalCameraId*/) override {
+ // no implementation yet.
+ return binder::Status::ok();
+ }
virtual ::android::binder::Status onTorchStatusChanged(
- int32_t status, const ::android::String16& cameraId) override;
+ int32_t status, const ::android::String16& cameraId) override;
virtual binder::Status onCameraAccessPrioritiesChanged() {
- // TODO: no implementation yet.
+ // TODO: no implementation yet. b/148146086
return binder::Status::ok();
}
};
diff --git a/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.cpp b/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.cpp
index 675ad24..bf89ca5 100644
--- a/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.cpp
+++ b/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.cpp
@@ -201,8 +201,9 @@
return HStatus::ILLEGAL_ARGUMENT;
}
+ std::vector<int> offlineStreamIds;
binder::Status ret = mDeviceRemote->endConfigure(convertFromHidl(operatingMode),
- cameraMetadata);
+ cameraMetadata, &offlineStreamIds);
return B2HStatus(ret);
}
diff --git a/services/camera/libcameraservice/tests/Android.mk b/services/camera/libcameraservice/tests/Android.mk
index ec5e876..fa5d69e 100644
--- a/services/camera/libcameraservice/tests/Android.mk
+++ b/services/camera/libcameraservice/tests/Android.mk
@@ -25,15 +25,21 @@
liblog \
libcamera_client \
libcamera_metadata \
+ libui \
libutils \
libjpeg \
libexif \
android.hardware.camera.common@1.0 \
android.hardware.camera.provider@2.4 \
android.hardware.camera.provider@2.5 \
+ android.hardware.camera.provider@2.6 \
android.hardware.camera.device@1.0 \
android.hardware.camera.device@3.2 \
- android.hardware.camera.device@3.4
+ android.hardware.camera.device@3.4 \
+ android.hidl.token@1.0-utils
+
+LOCAL_STATIC_LIBRARIES := \
+ libgmock
LOCAL_C_INCLUDES += \
system/media/private/camera/include \
diff --git a/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp b/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
index 084dc62..a8f6889 100644
--- a/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
+++ b/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
@@ -236,6 +236,8 @@
void onDeviceStatusChanged(const String8 &,
hardware::camera::common::V1_0::CameraDeviceStatus) override {}
+ void onDeviceStatusChanged(const String8 &, const String8 &,
+ hardware::camera::common::V1_0::CameraDeviceStatus) override {}
void onTorchStatusChanged(const String8 &,
hardware::camera::common::V1_0::TorchModeStatus) override {}
void onNewProviderRegistered() override {}
diff --git a/services/camera/libcameraservice/tests/DepthProcessorTest.cpp b/services/camera/libcameraservice/tests/DepthProcessorTest.cpp
index 2162514..673c149 100644
--- a/services/camera/libcameraservice/tests/DepthProcessorTest.cpp
+++ b/services/camera/libcameraservice/tests/DepthProcessorTest.cpp
@@ -20,7 +20,6 @@
#include <array>
#include <random>
-#include <dlfcn.h>
#include <gtest/gtest.h>
#include "../common/DepthPhotoProcessor.h"
@@ -36,19 +35,6 @@
static const size_t kTestBufferDepthSize (kTestBufferWidth * kTestBufferHeight);
static const size_t kSeed = 1234;
-void linkToDepthPhotoLibrary(void **libHandle /*out*/,
- process_depth_photo_frame *processFrameFunc /*out*/) {
- ASSERT_NE(libHandle, nullptr);
- ASSERT_NE(processFrameFunc, nullptr);
-
- *libHandle = dlopen(kDepthPhotoLibrary, RTLD_NOW | RTLD_LOCAL);
- if (*libHandle != nullptr) {
- *processFrameFunc = reinterpret_cast<camera3::process_depth_photo_frame> (
- dlsym(*libHandle, kDepthPhotoProcessFunction));
- ASSERT_NE(*processFrameFunc, nullptr);
- }
-}
-
void generateColorJpegBuffer(int jpegQuality, ExifOrientation orientationValue, bool includeExif,
bool switchDimensions, std::vector<uint8_t> *colorJpegBuffer /*out*/) {
ASSERT_NE(colorJpegBuffer, nullptr);
@@ -91,26 +77,9 @@
}
}
-TEST(DepthProcessorTest, LinkToLibray) {
- void *libHandle;
- process_depth_photo_frame processFunc;
- linkToDepthPhotoLibrary(&libHandle, &processFunc);
- if (libHandle != nullptr) {
- dlclose(libHandle);
- }
-}
-
TEST(DepthProcessorTest, BadInput) {
- void *libHandle;
int jpegQuality = 95;
- process_depth_photo_frame processFunc;
- linkToDepthPhotoLibrary(&libHandle, &processFunc);
- if (libHandle == nullptr) {
- // Depth library no present, nothing more to test.
- return;
- }
-
DepthPhotoInputFrame inputFrame;
// Worst case both depth and confidence maps have the same size as the main color image.
inputFrame.mMaxJpegSize = inputFrame.mMainJpegSize * 3;
@@ -128,37 +97,27 @@
inputFrame.mMainJpegWidth = kTestBufferWidth;
inputFrame.mMainJpegHeight = kTestBufferHeight;
inputFrame.mJpegQuality = jpegQuality;
- ASSERT_NE(processFunc(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(),
+ ASSERT_NE(processDepthPhotoFrame(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(),
&actualDepthPhotoSize), 0);
inputFrame.mMainJpegBuffer = reinterpret_cast<const char*> (colorJpegBuffer.data());
inputFrame.mMainJpegSize = colorJpegBuffer.size();
- ASSERT_NE(processFunc(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(),
+ ASSERT_NE(processDepthPhotoFrame(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(),
&actualDepthPhotoSize), 0);
inputFrame.mDepthMapBuffer = depth16Buffer.data();
inputFrame.mDepthMapWidth = inputFrame.mDepthMapStride = kTestBufferWidth;
inputFrame.mDepthMapHeight = kTestBufferHeight;
- ASSERT_NE(processFunc(inputFrame, depthPhotoBuffer.size(), nullptr,
+ ASSERT_NE(processDepthPhotoFrame(inputFrame, depthPhotoBuffer.size(), nullptr,
&actualDepthPhotoSize), 0);
- ASSERT_NE(processFunc(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(), nullptr),
- 0);
-
- dlclose(libHandle);
+ ASSERT_NE(processDepthPhotoFrame(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(),
+ nullptr), 0);
}
TEST(DepthProcessorTest, BasicDepthPhotoValidation) {
- void *libHandle;
int jpegQuality = 95;
- process_depth_photo_frame processFunc;
- linkToDepthPhotoLibrary(&libHandle, &processFunc);
- if (libHandle == nullptr) {
- // Depth library no present, nothing more to test.
- return;
- }
-
std::vector<uint8_t> colorJpegBuffer;
generateColorJpegBuffer(jpegQuality, ExifOrientation::ORIENTATION_UNDEFINED,
/*includeExif*/ false, /*switchDimensions*/ false, &colorJpegBuffer);
@@ -180,7 +139,7 @@
std::vector<uint8_t> depthPhotoBuffer(inputFrame.mMaxJpegSize);
size_t actualDepthPhotoSize = 0;
- ASSERT_EQ(processFunc(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(),
+ ASSERT_EQ(processDepthPhotoFrame(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(),
&actualDepthPhotoSize), 0);
ASSERT_TRUE((actualDepthPhotoSize > 0) && (depthPhotoBuffer.size() >= actualDepthPhotoSize));
@@ -196,21 +155,11 @@
ASSERT_EQ(NV12Compressor::findJpegSize(depthPhotoBuffer.data() + mainJpegSize,
actualDepthPhotoSize - mainJpegSize, &depthMapSize), OK);
ASSERT_TRUE((depthMapSize > 0) && (depthMapSize < (actualDepthPhotoSize - mainJpegSize)));
-
- dlclose(libHandle);
}
TEST(DepthProcessorTest, TestDepthPhotoExifOrientation) {
- void *libHandle;
int jpegQuality = 95;
- process_depth_photo_frame processFunc;
- linkToDepthPhotoLibrary(&libHandle, &processFunc);
- if (libHandle == nullptr) {
- // Depth library no present, nothing more to test.
- return;
- }
-
ExifOrientation exifOrientations[] = { ExifOrientation::ORIENTATION_UNDEFINED,
ExifOrientation::ORIENTATION_0_DEGREES, ExifOrientation::ORIENTATION_90_DEGREES,
ExifOrientation::ORIENTATION_180_DEGREES, ExifOrientation::ORIENTATION_270_DEGREES };
@@ -242,8 +191,8 @@
std::vector<uint8_t> depthPhotoBuffer(inputFrame.mMaxJpegSize);
size_t actualDepthPhotoSize = 0;
- ASSERT_EQ(processFunc(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(),
- &actualDepthPhotoSize), 0);
+ ASSERT_EQ(processDepthPhotoFrame(inputFrame, depthPhotoBuffer.size(),
+ depthPhotoBuffer.data(), &actualDepthPhotoSize), 0);
ASSERT_TRUE((actualDepthPhotoSize > 0) &&
(depthPhotoBuffer.size() >= actualDepthPhotoSize));
@@ -281,21 +230,11 @@
ASSERT_EQ(confidenceJpegExifOrientation, exifOrientation);
}
}
-
- dlclose(libHandle);
}
TEST(DepthProcessorTest, TestDephtPhotoPhysicalRotation) {
- void *libHandle;
int jpegQuality = 95;
- process_depth_photo_frame processFunc;
- linkToDepthPhotoLibrary(&libHandle, &processFunc);
- if (libHandle == nullptr) {
- // Depth library no present, nothing more to test.
- return;
- }
-
// In case of physical rotation, the EXIF orientation must always be 0.
auto exifOrientation = ExifOrientation::ORIENTATION_0_DEGREES;
DepthPhotoOrientation depthOrientations[] = {
@@ -339,8 +278,8 @@
std::vector<uint8_t> depthPhotoBuffer(inputFrame.mMaxJpegSize);
size_t actualDepthPhotoSize = 0;
- ASSERT_EQ(processFunc(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(),
- &actualDepthPhotoSize), 0);
+ ASSERT_EQ(processDepthPhotoFrame(inputFrame, depthPhotoBuffer.size(),
+ depthPhotoBuffer.data(), &actualDepthPhotoSize), 0);
ASSERT_TRUE((actualDepthPhotoSize > 0) &&
(depthPhotoBuffer.size() >= actualDepthPhotoSize));
@@ -377,6 +316,4 @@
ASSERT_EQ(confidenceMapWidth, expectedWidth);
ASSERT_EQ(confidenceMapHeight, expectedHeight);
}
-
- dlclose(libHandle);
}
diff --git a/services/camera/libcameraservice/tests/RotateAndCropMapperTest.cpp b/services/camera/libcameraservice/tests/RotateAndCropMapperTest.cpp
new file mode 100644
index 0000000..c638d40
--- /dev/null
+++ b/services/camera/libcameraservice/tests/RotateAndCropMapperTest.cpp
@@ -0,0 +1,403 @@
+/*
+ * 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 "RotateAndCropMapperTest"
+
+#include <functional>
+#include <random>
+
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+#include "../device3/RotateAndCropMapper.h"
+
+namespace rotateAndCropMapperTest {
+
+using namespace android;
+using namespace android::camera3;
+
+using ::testing::ElementsAreArray;
+using ::testing::Each;
+using ::testing::AllOf;
+using ::testing::Ge;
+using ::testing::Le;
+
+#define EXPECT_EQUAL_WITHIN_N(vec, array, N, msg) \
+{ \
+ std::vector<int32_t> vec_diff; \
+ std::transform(vec.begin(), vec.end(), array, \
+ std::back_inserter(vec_diff), std::minus()); \
+ EXPECT_THAT(vec_diff, Each(AllOf(Ge(-N), Le(N)))) << msg; \
+}
+
+int32_t testActiveArray[] = {100, 100, 4000, 3000};
+
+std::vector<uint8_t> basicModes = {
+ ANDROID_SCALER_ROTATE_AND_CROP_NONE,
+ ANDROID_SCALER_ROTATE_AND_CROP_90,
+ ANDROID_SCALER_ROTATE_AND_CROP_AUTO
+};
+
+CameraMetadata setupDeviceInfo(int32_t activeArray[4], std::vector<uint8_t> availableCropModes ) {
+ CameraMetadata deviceInfo;
+
+ deviceInfo.update(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
+ activeArray, 4);
+
+ deviceInfo.update(ANDROID_SCALER_AVAILABLE_ROTATE_AND_CROP_MODES,
+ availableCropModes.data(), availableCropModes.size());
+
+ return deviceInfo;
+}
+
+TEST(RotationMapperTest, Initialization) {
+ CameraMetadata deviceInfo = setupDeviceInfo(testActiveArray,
+ {ANDROID_SCALER_ROTATE_AND_CROP_NONE});
+
+ ASSERT_FALSE(RotateAndCropMapper::isNeeded(&deviceInfo));
+
+ deviceInfo.update(ANDROID_SCALER_AVAILABLE_ROTATE_AND_CROP_MODES,
+ basicModes.data(), 3);
+
+ ASSERT_TRUE(RotateAndCropMapper::isNeeded(&deviceInfo));
+}
+
+TEST(RotationMapperTest, IdentityTransform) {
+ status_t res;
+
+ CameraMetadata deviceInfo = setupDeviceInfo(testActiveArray,
+ basicModes);
+
+ RotateAndCropMapper mapper(&deviceInfo);
+
+ CameraMetadata request;
+ uint8_t mode = ANDROID_SCALER_ROTATE_AND_CROP_NONE;
+ auto full_crop = std::vector<int32_t>{0,0, testActiveArray[2], testActiveArray[3]};
+ auto full_region = std::vector<int32_t>{0,0, testActiveArray[2], testActiveArray[3], 1};
+ request.update(ANDROID_SCALER_ROTATE_AND_CROP,
+ &mode, 1);
+ request.update(ANDROID_SCALER_CROP_REGION,
+ full_crop.data(), full_crop.size());
+ request.update(ANDROID_CONTROL_AE_REGIONS,
+ full_region.data(), full_region.size());
+
+ // Map to HAL
+
+ res = mapper.updateCaptureRequest(&request);
+ ASSERT_TRUE(res == OK);
+
+ auto e = request.find(ANDROID_CONTROL_AE_REGIONS);
+ EXPECT_THAT(full_region, ElementsAreArray(e.data.i32, e.count));
+
+ e = request.find(ANDROID_SCALER_CROP_REGION);
+ EXPECT_THAT(full_crop, ElementsAreArray(e.data.i32, e.count));
+
+ // Add fields in HAL
+
+ CameraMetadata result(request);
+
+ auto face = std::vector<int32_t> {300,300,500,500};
+ result.update(ANDROID_STATISTICS_FACE_RECTANGLES,
+ face.data(), face.size());
+
+ // Map to app
+
+ res = mapper.updateCaptureResult(&result);
+ ASSERT_TRUE(res == OK);
+
+ e = result.find(ANDROID_CONTROL_AE_REGIONS);
+ EXPECT_THAT(full_region, ElementsAreArray(e.data.i32, e.count));
+
+ e = result.find(ANDROID_SCALER_CROP_REGION);
+ EXPECT_THAT(full_crop, ElementsAreArray(e.data.i32, e.count));
+
+ e = result.find(ANDROID_STATISTICS_FACE_RECTANGLES);
+ EXPECT_THAT(face, ElementsAreArray(e.data.i32, e.count));
+}
+
+TEST(RotationMapperTest, Transform90) {
+ status_t res;
+
+ CameraMetadata deviceInfo = setupDeviceInfo(testActiveArray,
+ basicModes);
+
+ RotateAndCropMapper mapper(&deviceInfo);
+
+ CameraMetadata request;
+ uint8_t mode = ANDROID_SCALER_ROTATE_AND_CROP_90;
+ auto full_crop = std::vector<int32_t> {0,0, testActiveArray[2], testActiveArray[3]};
+ auto full_region = std::vector<int32_t> {0,0, testActiveArray[2], testActiveArray[3], 1};
+ request.update(ANDROID_SCALER_ROTATE_AND_CROP,
+ &mode, 1);
+ request.update(ANDROID_SCALER_CROP_REGION,
+ full_crop.data(), full_crop.size());
+ request.update(ANDROID_CONTROL_AE_REGIONS,
+ full_region.data(), full_region.size());
+
+ // Map to HAL
+
+ res = mapper.updateCaptureRequest(&request);
+ ASSERT_TRUE(res == OK);
+
+ auto e = request.find(ANDROID_CONTROL_AE_REGIONS);
+ float aspectRatio = static_cast<float>(full_crop[2]) / full_crop[3];
+ int32_t rw = full_crop[3] / aspectRatio;
+ int32_t rh = full_crop[3];
+ auto rotated_region = std::vector<int32_t> {
+ full_crop[0] + (full_crop[2] - rw) / 2, full_crop[1],
+ full_crop[0] + (full_crop[2] + rw) / 2, full_crop[1] + full_crop[3],
+ 1
+ };
+ EXPECT_THAT(rotated_region, ElementsAreArray(e.data.i32, e.count))
+ << "Rotated AE region isn't right";
+
+ e = request.find(ANDROID_SCALER_CROP_REGION);
+ EXPECT_THAT(full_crop, ElementsAreArray(e.data.i32, e.count))
+ << "Rotated crop region isn't right";
+
+ // Add fields in HAL
+
+ CameraMetadata result(request);
+
+ auto face = std::vector<int32_t> {
+ rotated_region[0] + rw / 4, rotated_region[1] + rh / 4,
+ rotated_region[2] - rw / 4, rotated_region[3] - rh / 4};
+ result.update(ANDROID_STATISTICS_FACE_RECTANGLES,
+ face.data(), face.size());
+
+ auto landmarks = std::vector<int32_t> {
+ rotated_region[0], rotated_region[1],
+ rotated_region[2], rotated_region[3],
+ rotated_region[0] + rw / 4, rotated_region[1] + rh / 4,
+ rotated_region[0] + rw / 2, rotated_region[1] + rh / 2,
+ rotated_region[2] - rw / 4, rotated_region[3] - rh / 4
+ };
+ result.update(ANDROID_STATISTICS_FACE_LANDMARKS,
+ landmarks.data(), landmarks.size());
+
+ // Map to app
+
+ res = mapper.updateCaptureResult(&result);
+ ASSERT_TRUE(res == OK);
+
+ // Round-trip results can't be exact since we've gone from a large int range -> small int range
+ // and back, leading to quantization. For 4/3 aspect ratio, no more than +-1 error expected
+ e = result.find(ANDROID_CONTROL_AE_REGIONS);
+ EXPECT_EQUAL_WITHIN_N(full_region, e.data.i32, 1, "Round-tripped AE region isn't right");
+
+ e = result.find(ANDROID_SCALER_CROP_REGION);
+ EXPECT_EQUAL_WITHIN_N(full_crop, e.data.i32, 1, "Round-tripped crop region isn't right");
+
+ auto full_face = std::vector<int32_t> {
+ full_crop[0] + full_crop[2]/4, full_crop[1] + full_crop[3]/4,
+ full_crop[0] + 3*full_crop[2]/4, full_crop[1] + 3*full_crop[3]/4
+ };
+ e = result.find(ANDROID_STATISTICS_FACE_RECTANGLES);
+ EXPECT_EQUAL_WITHIN_N(full_face, e.data.i32, 1, "App-side face rectangle isn't right");
+
+ auto full_landmarks = std::vector<int32_t> {
+ full_crop[0], full_crop[1] + full_crop[3],
+ full_crop[0] + full_crop[2], full_crop[1],
+ full_crop[0] + full_crop[2]/4, full_crop[1] + 3*full_crop[3]/4,
+ full_crop[0] + full_crop[2]/2, full_crop[1] + full_crop[3]/2,
+ full_crop[0] + 3*full_crop[2]/4, full_crop[1] + full_crop[3]/4
+ };
+ e = result.find(ANDROID_STATISTICS_FACE_LANDMARKS);
+ EXPECT_EQUAL_WITHIN_N(full_landmarks, e.data.i32, 1, "App-side face landmarks aren't right");
+}
+
+TEST(RotationMapperTest, Transform270) {
+ status_t res;
+
+ CameraMetadata deviceInfo = setupDeviceInfo(testActiveArray,
+ basicModes);
+
+ RotateAndCropMapper mapper(&deviceInfo);
+
+ CameraMetadata request;
+ uint8_t mode = ANDROID_SCALER_ROTATE_AND_CROP_270;
+ auto full_crop = std::vector<int32_t> {0,0, testActiveArray[2], testActiveArray[3]};
+ auto full_region = std::vector<int32_t> {0,0, testActiveArray[2], testActiveArray[3], 1};
+ request.update(ANDROID_SCALER_ROTATE_AND_CROP,
+ &mode, 1);
+ request.update(ANDROID_SCALER_CROP_REGION,
+ full_crop.data(), full_crop.size());
+ request.update(ANDROID_CONTROL_AE_REGIONS,
+ full_region.data(), full_region.size());
+
+ // Map to HAL
+
+ res = mapper.updateCaptureRequest(&request);
+ ASSERT_TRUE(res == OK);
+
+ auto e = request.find(ANDROID_CONTROL_AE_REGIONS);
+ float aspectRatio = static_cast<float>(full_crop[2]) / full_crop[3];
+ int32_t rw = full_crop[3] / aspectRatio;
+ int32_t rh = full_crop[3];
+ auto rotated_region = std::vector<int32_t> {
+ full_crop[0] + (full_crop[2] - rw) / 2, full_crop[1],
+ full_crop[0] + (full_crop[2] + rw) / 2, full_crop[1] + full_crop[3],
+ 1
+ };
+ EXPECT_THAT(rotated_region, ElementsAreArray(e.data.i32, e.count))
+ << "Rotated AE region isn't right";
+
+ e = request.find(ANDROID_SCALER_CROP_REGION);
+ EXPECT_THAT(full_crop, ElementsAreArray(e.data.i32, e.count))
+ << "Rotated crop region isn't right";
+
+ // Add fields in HAL
+
+ CameraMetadata result(request);
+
+ auto face = std::vector<int32_t> {
+ rotated_region[0] + rw / 4, rotated_region[1] + rh / 4,
+ rotated_region[2] - rw / 4, rotated_region[3] - rh / 4};
+ result.update(ANDROID_STATISTICS_FACE_RECTANGLES,
+ face.data(), face.size());
+
+ auto landmarks = std::vector<int32_t> {
+ rotated_region[0], rotated_region[1],
+ rotated_region[2], rotated_region[3],
+ rotated_region[0] + rw / 4, rotated_region[1] + rh / 4,
+ rotated_region[0] + rw / 2, rotated_region[1] + rh / 2,
+ rotated_region[2] - rw / 4, rotated_region[3] - rh / 4
+ };
+ result.update(ANDROID_STATISTICS_FACE_LANDMARKS,
+ landmarks.data(), landmarks.size());
+
+ // Map to app
+
+ res = mapper.updateCaptureResult(&result);
+ ASSERT_TRUE(res == OK);
+
+ // Round-trip results can't be exact since we've gone from a large int range -> small int range
+ // and back, leading to quantization. For 4/3 aspect ratio, no more than +-1 error expected
+
+ e = result.find(ANDROID_CONTROL_AE_REGIONS);
+ EXPECT_EQUAL_WITHIN_N(full_region, e.data.i32, 1, "Round-tripped AE region isn't right");
+
+ e = result.find(ANDROID_SCALER_CROP_REGION);
+ EXPECT_EQUAL_WITHIN_N(full_crop, e.data.i32, 1, "Round-tripped crop region isn't right");
+
+ auto full_face = std::vector<int32_t> {
+ full_crop[0] + full_crop[2]/4, full_crop[1] + full_crop[3]/4,
+ full_crop[0] + 3*full_crop[2]/4, full_crop[1] + 3*full_crop[3]/4
+ };
+ e = result.find(ANDROID_STATISTICS_FACE_RECTANGLES);
+ EXPECT_EQUAL_WITHIN_N(full_face, e.data.i32, 1, "App-side face rectangle isn't right");
+
+ auto full_landmarks = std::vector<int32_t> {
+ full_crop[0] + full_crop[2], full_crop[1],
+ full_crop[0], full_crop[1] + full_crop[3],
+ full_crop[0] + 3*full_crop[2]/4, full_crop[1] + full_crop[3]/4,
+ full_crop[0] + full_crop[2]/2, full_crop[1] + full_crop[3]/2,
+ full_crop[0] + full_crop[2]/4, full_crop[1] + 3*full_crop[3]/4
+ };
+ e = result.find(ANDROID_STATISTICS_FACE_LANDMARKS);
+ EXPECT_EQUAL_WITHIN_N(full_landmarks, e.data.i32, 1, "App-side face landmarks aren't right");
+}
+
+TEST(RotationMapperTest, Transform180) {
+ status_t res;
+
+ CameraMetadata deviceInfo = setupDeviceInfo(testActiveArray,
+ basicModes);
+
+ RotateAndCropMapper mapper(&deviceInfo);
+
+ CameraMetadata request;
+ uint8_t mode = ANDROID_SCALER_ROTATE_AND_CROP_180;
+ auto full_crop = std::vector<int32_t> {0,0, testActiveArray[2], testActiveArray[3]};
+ auto full_region = std::vector<int32_t> {0,0, testActiveArray[2], testActiveArray[3], 1};
+ request.update(ANDROID_SCALER_ROTATE_AND_CROP,
+ &mode, 1);
+ request.update(ANDROID_SCALER_CROP_REGION,
+ full_crop.data(), full_crop.size());
+ request.update(ANDROID_CONTROL_AE_REGIONS,
+ full_region.data(), full_region.size());
+
+ // Map to HAL
+
+ res = mapper.updateCaptureRequest(&request);
+ ASSERT_TRUE(res == OK);
+
+ auto e = request.find(ANDROID_CONTROL_AE_REGIONS);
+ auto rotated_region = full_region;
+ EXPECT_THAT(rotated_region, ElementsAreArray(e.data.i32, e.count))
+ << "Rotated AE region isn't right";
+
+ e = request.find(ANDROID_SCALER_CROP_REGION);
+ EXPECT_THAT(full_crop, ElementsAreArray(e.data.i32, e.count))
+ << "Rotated crop region isn't right";
+
+ // Add fields in HAL
+
+ CameraMetadata result(request);
+
+ float rw = full_region[2] - full_region[0];
+ float rh = full_region[3] - full_region[1];
+ auto face = std::vector<int32_t> {
+ rotated_region[0] + (int)(rw / 4), rotated_region[1] + (int)(rh / 4),
+ rotated_region[2] - (int)(rw / 4), rotated_region[3] - (int)(rh / 4)
+ };
+ result.update(ANDROID_STATISTICS_FACE_RECTANGLES,
+ face.data(), face.size());
+
+ auto landmarks = std::vector<int32_t> {
+ rotated_region[0], rotated_region[1],
+ rotated_region[2], rotated_region[3],
+ rotated_region[0] + (int)(rw / 4), rotated_region[1] + (int)(rh / 4),
+ rotated_region[0] + (int)(rw / 2), rotated_region[1] + (int)(rh / 2),
+ rotated_region[2] - (int)(rw / 4), rotated_region[3] - (int)(rh / 4)
+ };
+ result.update(ANDROID_STATISTICS_FACE_LANDMARKS,
+ landmarks.data(), landmarks.size());
+
+ // Map to app
+
+ res = mapper.updateCaptureResult(&result);
+ ASSERT_TRUE(res == OK);
+
+ e = result.find(ANDROID_CONTROL_AE_REGIONS);
+ EXPECT_THAT(full_region, ElementsAreArray(e.data.i32, e.count))
+ << "Round-tripped AE region isn't right";
+
+ e = result.find(ANDROID_SCALER_CROP_REGION);
+ EXPECT_THAT(full_crop, ElementsAreArray(e.data.i32, e.count))
+ << "Round-tripped crop region isn't right";
+
+ auto full_face = std::vector<int32_t> {
+ full_crop[0] + full_crop[2]/4, full_crop[1] + full_crop[3]/4,
+ full_crop[0] + 3*full_crop[2]/4, full_crop[1] + 3*full_crop[3]/4
+ };
+ e = result.find(ANDROID_STATISTICS_FACE_RECTANGLES);
+ EXPECT_EQUAL_WITHIN_N(full_face, e.data.i32, 1, "App-side face rectangle isn't right");
+
+ auto full_landmarks = std::vector<int32_t> {
+ full_crop[0] + full_crop[2], full_crop[1] + full_crop[3],
+ full_crop[0], full_crop[1],
+ full_crop[0] + 3*full_crop[2]/4, full_crop[1] + 3*full_crop[3]/4,
+ full_crop[0] + full_crop[2]/2, full_crop[1] + full_crop[3]/2,
+ full_crop[0] + full_crop[2]/4, full_crop[1] + full_crop[3]/4
+ };
+ e = result.find(ANDROID_STATISTICS_FACE_LANDMARKS);
+ EXPECT_EQUAL_WITHIN_N(full_landmarks, e.data.i32, 1, "App-side face landmarks aren't right");
+}
+
+
+} // namespace rotateAndCropMapperTest
diff --git a/services/camera/libcameraservice/tests/ZoomRatioTest.cpp b/services/camera/libcameraservice/tests/ZoomRatioTest.cpp
new file mode 100644
index 0000000..300da09
--- /dev/null
+++ b/services/camera/libcameraservice/tests/ZoomRatioTest.cpp
@@ -0,0 +1,457 @@
+/*
+ * Copyright (C) 2019 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 "ZoomRatioMapperTest"
+
+#include <gtest/gtest.h>
+#include <utils/Errors.h>
+
+#include "../device3/ZoomRatioMapper.h"
+
+using namespace std;
+using namespace android;
+using namespace android::camera3;
+
+constexpr int kMaxAllowedPixelError = 1;
+constexpr float kMaxAllowedRatioError = 0.1;
+
+constexpr int32_t testActiveArraySize[] = {100, 100, 1024, 768};
+constexpr int32_t testPreCorrActiveArraySize[] = {90, 90, 1044, 788};
+
+constexpr int32_t testDefaultCropSize[][4] = {
+ {0, 0, 1024, 768}, // active array default crop
+ {0, 0, 1044, 788}, // preCorrection active array default crop
+};
+
+constexpr int32_t test2xCropRegion[][4] = {
+ {256, 192, 512, 384}, // active array 2x zoom crop
+ {261, 197, 522, 394}, // preCorrection active array default crop
+};
+
+constexpr int32_t testLetterBoxSize[][4] = {
+ {0, 96, 1024, 576}, // active array 2x zoom crop
+ {0, 106, 1024, 576}, // preCorrection active array default crop
+};
+
+status_t setupTestMapper(ZoomRatioMapper *m, float maxDigitalZoom,
+ const int32_t activeArray[4], const int32_t preCorrectArray[4],
+ bool hasZoomRatioRange, float zoomRatioRange[2],
+ bool usePreCorrectArray) {
+ CameraMetadata deviceInfo;
+
+ deviceInfo.update(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, activeArray, 4);
+ deviceInfo.update(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, preCorrectArray, 4);
+ deviceInfo.update(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM, &maxDigitalZoom, 1);
+ if (hasZoomRatioRange) {
+ deviceInfo.update(ANDROID_CONTROL_ZOOM_RATIO_RANGE, zoomRatioRange, 2);
+ }
+
+ bool supportNativeZoomRatio;
+ status_t res = ZoomRatioMapper::overrideZoomRatioTags(&deviceInfo, &supportNativeZoomRatio);
+ if (res != OK) {
+ return res;
+ }
+
+ *m = ZoomRatioMapper(&deviceInfo, hasZoomRatioRange, usePreCorrectArray);
+ return OK;
+}
+
+TEST(ZoomRatioTest, Initialization) {
+ CameraMetadata deviceInfo;
+ status_t res;
+ camera_metadata_entry_t entry;
+
+ deviceInfo.update(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE,
+ testPreCorrActiveArraySize, 4);
+ deviceInfo.update(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, testActiveArraySize, 4);
+
+ // Test initialization from devices not supporting zoomRange
+ float maxDigitalZoom = 4.0f;
+ ZoomRatioMapper mapperNoZoomRange;
+ deviceInfo.update(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM, &maxDigitalZoom, 1);
+ bool supportNativeZoomRatio;
+ res = ZoomRatioMapper::overrideZoomRatioTags(&deviceInfo, &supportNativeZoomRatio);
+ ASSERT_EQ(res, OK);
+ ASSERT_EQ(supportNativeZoomRatio, false);
+ mapperNoZoomRange = ZoomRatioMapper(&deviceInfo,
+ supportNativeZoomRatio, true/*usePreCorrectArray*/);
+ ASSERT_TRUE(mapperNoZoomRange.isValid());
+ mapperNoZoomRange = ZoomRatioMapper(&deviceInfo,
+ supportNativeZoomRatio, false/*usePreCorrectArray*/);
+ ASSERT_TRUE(mapperNoZoomRange.isValid());
+
+ entry = deviceInfo.find(ANDROID_CONTROL_ZOOM_RATIO_RANGE);
+ ASSERT_EQ(entry.count, 2U);
+ ASSERT_EQ(entry.data.f[0], 1.0);
+ ASSERT_EQ(entry.data.f[1], maxDigitalZoom);
+
+ entry = deviceInfo.find(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS);
+ ASSERT_GT(entry.count, 0U);
+ ASSERT_NE(std::find(entry.data.i32, entry.data.i32 + entry.count,
+ ANDROID_CONTROL_ZOOM_RATIO_RANGE), entry.data.i32 + entry.count);
+
+ entry = deviceInfo.find(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS);
+ ASSERT_GT(entry.count, 0U);
+ ASSERT_NE(std::find(entry.data.i32, entry.data.i32 + entry.count,
+ ANDROID_CONTROL_ZOOM_RATIO), entry.data.i32 + entry.count);
+
+ entry = deviceInfo.find(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS);
+ ASSERT_GT(entry.count, 0U);
+ ASSERT_NE(std::find(entry.data.i32, entry.data.i32 + entry.count,
+ ANDROID_CONTROL_ZOOM_RATIO), entry.data.i32 + entry.count);
+
+ // Test initialization from devices supporting zoomRange
+ float ratioRange[2] = {0.2f, maxDigitalZoom};
+ deviceInfo.update(ANDROID_CONTROL_ZOOM_RATIO_RANGE, ratioRange, 2);
+ res = ZoomRatioMapper::overrideZoomRatioTags(&deviceInfo, &supportNativeZoomRatio);
+ ASSERT_EQ(res, OK);
+ ASSERT_EQ(supportNativeZoomRatio, true);
+ ZoomRatioMapper mapperWithZoomRange;
+ mapperWithZoomRange = ZoomRatioMapper(&deviceInfo,
+ supportNativeZoomRatio, true/*usePreCorrectArray*/);
+ ASSERT_TRUE(mapperWithZoomRange.isValid());
+ mapperWithZoomRange = ZoomRatioMapper(&deviceInfo,
+ supportNativeZoomRatio, false/*usePreCorrectArray*/);
+ ASSERT_TRUE(mapperWithZoomRange.isValid());
+
+ entry = deviceInfo.find(ANDROID_CONTROL_ZOOM_RATIO_RANGE);
+ ASSERT_EQ(entry.count, 2U);
+ ASSERT_EQ(entry.data.f[0], ratioRange[0]);
+ ASSERT_EQ(entry.data.f[1], ratioRange[1]);
+
+ // Test default zoom ratio in template
+ CameraMetadata requestTemplate;
+ res = ZoomRatioMapper::initZoomRatioInTemplate(&requestTemplate);
+ ASSERT_EQ(res, OK);
+ entry = requestTemplate.find(ANDROID_CONTROL_ZOOM_RATIO);
+ ASSERT_EQ(entry.count, 1U);
+ ASSERT_EQ(entry.data.f[0], 1.0f);
+
+ float customRatio = 0.5f;
+ res = requestTemplate.update(ANDROID_CONTROL_ZOOM_RATIO, &customRatio, 1);
+ ASSERT_EQ(res, OK);
+ res = ZoomRatioMapper::initZoomRatioInTemplate(&requestTemplate);
+ ASSERT_EQ(res, OK);
+ entry = requestTemplate.find(ANDROID_CONTROL_ZOOM_RATIO);
+ ASSERT_EQ(entry.count, 1U);
+ ASSERT_EQ(entry.data.f[0], customRatio);
+}
+
+void subScaleCoordinatesTest(bool usePreCorrectArray) {
+ ZoomRatioMapper mapper;
+ float maxDigitalZoom = 4.0f;
+ float zoomRatioRange[2];
+ ASSERT_EQ(OK, setupTestMapper(&mapper, maxDigitalZoom,
+ testActiveArraySize, testPreCorrActiveArraySize,
+ false/*hasZoomRatioRange*/, zoomRatioRange,
+ usePreCorrectArray));
+
+ size_t index = 0;
+ int32_t width = testActiveArraySize[2];
+ int32_t height = testActiveArraySize[3];
+ if (usePreCorrectArray) {
+ index = 1;
+ width = testPreCorrActiveArraySize[2];
+ height = testPreCorrActiveArraySize[3];
+ }
+
+ std::array<int32_t, 16> originalCoords = {
+ 0, 0, // top-left
+ width, 0, // top-right
+ 0, height, // bottom-left
+ width, height, // bottom-right
+ width / 2, height / 2, // center
+ width / 4, height / 4, // top-left after 2x
+ width / 3, height * 2 / 3, // bottom-left after 3x zoom
+ width * 7 / 8, height / 2, // middle-right after 1.33x zoom
+ };
+
+ // Verify 1.0x zoom doesn't change the coordinates
+ auto coords = originalCoords;
+ mapper.scaleCoordinates(coords.data(), coords.size()/2, 1.0f, ZoomRatioMapper::ClampOff);
+ for (size_t i = 0; i < coords.size(); i++) {
+ EXPECT_EQ(coords[i], originalCoords[i]);
+ }
+
+ // Verify 2.0x zoom work as expected (no clamping)
+ std::array<float, 16> expected2xCoords = {
+ - width / 2.0f, - height / 2.0f,// top-left
+ width * 3 / 2.0f, - height / 2.0f, // top-right
+ - width / 2.0f, height * 3 / 2.0f, // bottom-left
+ width * 3 / 2.0f, height * 3 / 2.0f, // bottom-right
+ width / 2.0f, height / 2.0f, // center
+ 0, 0, // top-left after 2x
+ width / 6.0f, height - height / 6.0f, // bottom-left after 3x zoom
+ width + width / 4.0f, height / 2.0f, // middle-right after 1.33x zoom
+ };
+ coords = originalCoords;
+ mapper.scaleCoordinates(coords.data(), coords.size()/2, 2.0f, ZoomRatioMapper::ClampOff);
+ for (size_t i = 0; i < coords.size(); i++) {
+ EXPECT_LE(std::abs(coords[i] - expected2xCoords[i]), kMaxAllowedPixelError);
+ }
+
+ // Verify 2.0x zoom work as expected (with inclusive clamping)
+ std::array<float, 16> expected2xCoordsClampedInc = {
+ 0, 0, // top-left
+ static_cast<float>(width) - 1, 0, // top-right
+ 0, static_cast<float>(height) - 1, // bottom-left
+ static_cast<float>(width) - 1, static_cast<float>(height) - 1, // bottom-right
+ width / 2.0f, height / 2.0f, // center
+ 0, 0, // top-left after 2x
+ width / 6.0f, height - height / 6.0f , // bottom-left after 3x zoom
+ static_cast<float>(width) - 1, height / 2.0f, // middle-right after 1.33x zoom
+ };
+ coords = originalCoords;
+ mapper.scaleCoordinates(coords.data(), coords.size()/2, 2.0f, ZoomRatioMapper::ClampInclusive);
+ for (size_t i = 0; i < coords.size(); i++) {
+ EXPECT_LE(std::abs(coords[i] - expected2xCoordsClampedInc[i]), kMaxAllowedPixelError);
+ }
+
+ // Verify 2.0x zoom work as expected (with exclusive clamping)
+ std::array<float, 16> expected2xCoordsClampedExc = {
+ 0, 0, // top-left
+ static_cast<float>(width), 0, // top-right
+ 0, static_cast<float>(height), // bottom-left
+ static_cast<float>(width), static_cast<float>(height), // bottom-right
+ width / 2.0f, height / 2.0f, // center
+ 0, 0, // top-left after 2x
+ width / 6.0f, height - height / 6.0f , // bottom-left after 3x zoom
+ static_cast<float>(width), height / 2.0f, // middle-right after 1.33x zoom
+ };
+ coords = originalCoords;
+ mapper.scaleCoordinates(coords.data(), coords.size()/2, 2.0f, ZoomRatioMapper::ClampExclusive);
+ for (size_t i = 0; i < coords.size(); i++) {
+ EXPECT_LE(std::abs(coords[i] - expected2xCoordsClampedExc[i]), kMaxAllowedPixelError);
+ }
+
+ // Verify 0.33x zoom work as expected
+ std::array<float, 16> expectedZoomOutCoords = {
+ width / 3.0f, height / 3.0f, // top-left
+ width * 2 / 3.0f, height / 3.0f, // top-right
+ width / 3.0f, height * 2 / 3.0f, // bottom-left
+ width * 2 / 3.0f, height * 2 / 3.0f, // bottom-right
+ width / 2.0f, height / 2.0f, // center
+ width * 5 / 12.0f, height * 5 / 12.0f, // top-left after 2x
+ width * 4 / 9.0f, height * 5 / 9.0f, // bottom-left after 3x zoom-in
+ width * 5 / 8.0f, height / 2.0f, // middle-right after 1.33x zoom-in
+ };
+ coords = originalCoords;
+ mapper.scaleCoordinates(coords.data(), coords.size()/2, 1.0f/3, ZoomRatioMapper::ClampOff);
+ for (size_t i = 0; i < coords.size(); i++) {
+ EXPECT_LE(std::abs(coords[i] - expectedZoomOutCoords[i]), kMaxAllowedPixelError);
+ }
+}
+
+TEST(ZoomRatioTest, scaleCoordinatesTest) {
+ subScaleCoordinatesTest(false/*usePreCorrectArray*/);
+ subScaleCoordinatesTest(true/*usePreCorrectArray*/);
+}
+
+void subCropOverMaxDigitalZoomTest(bool usePreCorrectArray) {
+ status_t res;
+ ZoomRatioMapper mapper;
+ float noZoomRatioRange[2];
+ res = setupTestMapper(&mapper, 4.0/*maxDigitalZoom*/,
+ testActiveArraySize, testPreCorrActiveArraySize,
+ false/*hasZoomRatioRange*/, noZoomRatioRange,
+ usePreCorrectArray);
+ ASSERT_EQ(res, OK);
+
+ CameraMetadata metadata;
+ camera_metadata_entry_t entry;
+
+ size_t index = usePreCorrectArray ? 1 : 0;
+ metadata.update(ANDROID_SCALER_CROP_REGION, testDefaultCropSize[index], 4);
+ res = mapper.updateCaptureRequest(&metadata);
+ ASSERT_EQ(res, OK);
+ entry = metadata.find(ANDROID_SCALER_CROP_REGION);
+ ASSERT_EQ(entry.count, 4U);
+ for (int i = 0; i < 4; i ++) {
+ EXPECT_EQ(entry.data.i32[i], testDefaultCropSize[index][i]);
+ }
+
+ metadata.update(ANDROID_SCALER_CROP_REGION, test2xCropRegion[index], 4);
+ res = mapper.updateCaptureResult(&metadata, true/*requestedZoomRatioIs1*/);
+ ASSERT_EQ(res, OK);
+ entry = metadata.find(ANDROID_SCALER_CROP_REGION);
+ ASSERT_EQ(entry.count, 4U);
+ for (int i = 0; i < 4; i ++) {
+ EXPECT_EQ(entry.data.i32[i], test2xCropRegion[index][i]);
+ }
+ entry = metadata.find(ANDROID_CONTROL_ZOOM_RATIO);
+ ASSERT_TRUE(entry.count == 0 || (entry.count == 1 && entry.data.f[0] == 1.0f));
+}
+
+TEST(ZoomRatioTest, CropOverMaxDigitalZoomTest) {
+ subCropOverMaxDigitalZoomTest(false/*usePreCorrectArray*/);
+ subCropOverMaxDigitalZoomTest(true/*usePreCorrectArray*/);
+}
+
+void subCropOverZoomRangeTest(bool usePreCorrectArray) {
+ status_t res;
+ ZoomRatioMapper mapper;
+ float zoomRatioRange[2] = {0.5f, 4.0f};
+ res = setupTestMapper(&mapper, 4.0/*maxDigitalZoom*/,
+ testActiveArraySize, testPreCorrActiveArraySize,
+ true/*hasZoomRatioRange*/, zoomRatioRange,
+ usePreCorrectArray);
+ ASSERT_EQ(res, OK);
+
+ CameraMetadata metadata;
+ camera_metadata_entry_t entry;
+
+ size_t index = usePreCorrectArray ? 1 : 0;
+
+ // 2x zoom crop region, zoomRatio is 1.0f
+ metadata.update(ANDROID_SCALER_CROP_REGION, test2xCropRegion[index], 4);
+ res = mapper.updateCaptureRequest(&metadata);
+ ASSERT_EQ(res, OK);
+ entry = metadata.find(ANDROID_SCALER_CROP_REGION);
+ ASSERT_EQ(entry.count, 4U);
+ for (int i = 0; i < 4; i++) {
+ EXPECT_EQ(entry.data.i32[i], testDefaultCropSize[index][i]);
+ }
+ entry = metadata.find(ANDROID_CONTROL_ZOOM_RATIO);
+ EXPECT_NEAR(entry.data.f[0], 2.0f, kMaxAllowedRatioError);
+
+ res = mapper.updateCaptureResult(&metadata, true/*requestedZoomRatioIs1*/);
+ ASSERT_EQ(res, OK);
+ entry = metadata.find(ANDROID_CONTROL_ZOOM_RATIO);
+ EXPECT_NEAR(entry.data.f[0], 1.0f, kMaxAllowedRatioError);
+ entry = metadata.find(ANDROID_SCALER_CROP_REGION);
+ ASSERT_EQ(entry.count, 4U);
+ for (int i = 0; i < 4; i++) {
+ EXPECT_EQ(entry.data.i32[i], test2xCropRegion[index][i]);
+ }
+
+ // Letter boxing crop region, zoomRatio is 1.0
+ float zoomRatio = 1.0f;
+ metadata.update(ANDROID_CONTROL_ZOOM_RATIO, &zoomRatio, 1);
+ metadata.update(ANDROID_SCALER_CROP_REGION, testLetterBoxSize[index], 4);
+ res = mapper.updateCaptureRequest(&metadata);
+ ASSERT_EQ(res, OK);
+ entry = metadata.find(ANDROID_SCALER_CROP_REGION);
+ ASSERT_EQ(entry.count, 4U);
+ for (int i = 0; i < 4; i++) {
+ EXPECT_EQ(entry.data.i32[i], testLetterBoxSize[index][i]);
+ }
+ entry = metadata.find(ANDROID_CONTROL_ZOOM_RATIO);
+ EXPECT_NEAR(entry.data.f[0], 1.0f, kMaxAllowedRatioError);
+
+ res = mapper.updateCaptureResult(&metadata, true/*requestedZoomRatioIs1*/);
+ ASSERT_EQ(res, OK);
+ entry = metadata.find(ANDROID_SCALER_CROP_REGION);
+ ASSERT_EQ(entry.count, 4U);
+ for (int i = 0; i < 4; i++) {
+ EXPECT_EQ(entry.data.i32[i], testLetterBoxSize[index][i]);
+ }
+ entry = metadata.find(ANDROID_CONTROL_ZOOM_RATIO);
+ EXPECT_NEAR(entry.data.f[0], 1.0f, kMaxAllowedRatioError);
+}
+
+TEST(ZoomRatioTest, CropOverZoomRangeTest) {
+ subCropOverZoomRangeTest(false/*usePreCorrectArray*/);
+ subCropOverZoomRangeTest(true/*usePreCorrectArray*/);
+}
+
+void subZoomOverMaxDigitalZoomTest(bool usePreCorrectArray) {
+ status_t res;
+ ZoomRatioMapper mapper;
+ float noZoomRatioRange[2];
+ res = setupTestMapper(&mapper, 4.0/*maxDigitalZoom*/,
+ testActiveArraySize, testPreCorrActiveArraySize,
+ false/*hasZoomRatioRange*/, noZoomRatioRange,
+ usePreCorrectArray);
+ ASSERT_EQ(res, OK);
+
+ CameraMetadata metadata;
+ float zoomRatio = 3.0f;
+ camera_metadata_entry_t entry;
+
+ size_t index = usePreCorrectArray ? 1 : 0;
+
+ // Full active array crop, zoomRatio is 3.0f
+ metadata.update(ANDROID_SCALER_CROP_REGION, testDefaultCropSize[index], 4);
+ metadata.update(ANDROID_CONTROL_ZOOM_RATIO, &zoomRatio, 1);
+ res = mapper.updateCaptureRequest(&metadata);
+ ASSERT_EQ(res, OK);
+ entry = metadata.find(ANDROID_SCALER_CROP_REGION);
+ ASSERT_EQ(entry.count, 4U);
+ std::array<float, 4> expectedCrop = {
+ testDefaultCropSize[index][2] / 3.0f, /*x*/
+ testDefaultCropSize[index][3] / 3.0f, /*y*/
+ testDefaultCropSize[index][2] / 3.0f, /*width*/
+ testDefaultCropSize[index][3] / 3.0f, /*height*/
+ };
+ for (int i = 0; i < 4; i++) {
+ EXPECT_LE(std::abs(entry.data.i32[i] - expectedCrop[i]), kMaxAllowedPixelError);
+ }
+
+ entry = metadata.find(ANDROID_CONTROL_ZOOM_RATIO);
+ if (entry.count == 1) {
+ EXPECT_NEAR(entry.data.f[0], 1.0f, kMaxAllowedRatioError);
+ }
+}
+
+TEST(ZoomRatioTest, ZoomOverMaxDigitalZoomTest) {
+ subZoomOverMaxDigitalZoomTest(false/*usePreCorrectArray*/);
+ subZoomOverMaxDigitalZoomTest(true/*usePreCorrectArray*/);
+}
+
+void subZoomOverZoomRangeTest(bool usePreCorrectArray) {
+ status_t res;
+ ZoomRatioMapper mapper;
+ float zoomRatioRange[2] = {1.0f, 4.0f};
+ res = setupTestMapper(&mapper, 4.0/*maxDigitalZoom*/,
+ testActiveArraySize, testPreCorrActiveArraySize,
+ true/*hasZoomRatioRange*/, zoomRatioRange,
+ usePreCorrectArray);
+ ASSERT_EQ(res, OK);
+
+ CameraMetadata metadata;
+ float zoomRatio = 3.0f;
+ camera_metadata_entry_t entry;
+ size_t index = usePreCorrectArray ? 1 : 0;
+
+ // Full active array crop, zoomRatio is 3.0f
+ metadata.update(ANDROID_SCALER_CROP_REGION, testDefaultCropSize[index], 4);
+ metadata.update(ANDROID_CONTROL_ZOOM_RATIO, &zoomRatio, 1);
+ res = mapper.updateCaptureRequest(&metadata);
+ ASSERT_EQ(res, OK);
+ entry = metadata.find(ANDROID_SCALER_CROP_REGION);
+ ASSERT_EQ(entry.count, 4U);
+ for (int i = 0; i < 4; i ++) {
+ EXPECT_EQ(entry.data.i32[i], testDefaultCropSize[index][i]);
+ }
+ entry = metadata.find(ANDROID_CONTROL_ZOOM_RATIO);
+ ASSERT_EQ(entry.data.f[0], zoomRatio);
+
+ res = mapper.updateCaptureResult(&metadata, false/*requestedZoomRatioIs1*/);
+ ASSERT_EQ(res, OK);
+ entry = metadata.find(ANDROID_SCALER_CROP_REGION);
+ ASSERT_EQ(entry.count, 4U);
+ for (int i = 0; i < 4; i ++) {
+ EXPECT_EQ(entry.data.i32[i], testDefaultCropSize[index][i]);
+ }
+ entry = metadata.find(ANDROID_CONTROL_ZOOM_RATIO);
+ ASSERT_EQ(entry.data.f[0], zoomRatio);
+}
+
+TEST(ZoomRatioTest, ZoomOverZoomRangeTest) {
+ subZoomOverZoomRangeTest(false/*usePreCorrectArray*/);
+ subZoomOverZoomRangeTest(true/*usePreCorrectArray*/);
+}
diff --git a/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp b/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
new file mode 100644
index 0000000..888671c
--- /dev/null
+++ b/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+#include "SessionConfigurationUtils.h"
+#include "../api2/CameraDeviceClient.h"
+
+namespace android {
+
+binder::Status
+SessionConfigurationUtils::convertToHALStreamCombination(
+ const SessionConfiguration& sessionConfiguration,
+ const String8 &logicalCameraId, const CameraMetadata &deviceInfo,
+ metadataGetter getMetadata, const std::vector<std::string> &physicalCameraIds,
+ hardware::camera::device::V3_4::StreamConfiguration &streamConfiguration, bool *earlyExit) {
+ // TODO: http://b/148329298 Move the other dependencies from
+ // CameraDeviceClient into SessionConfigurationUtils.
+ return CameraDeviceClient::convertToHALStreamCombination(sessionConfiguration, logicalCameraId,
+ deviceInfo, getMetadata, physicalCameraIds, streamConfiguration, earlyExit);
+}
+
+}// namespace android
diff --git a/services/camera/libcameraservice/utils/SessionConfigurationUtils.h b/services/camera/libcameraservice/utils/SessionConfigurationUtils.h
new file mode 100644
index 0000000..fb519d9
--- /dev/null
+++ b/services/camera/libcameraservice/utils/SessionConfigurationUtils.h
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+#ifndef ANDROID_SERVERS_CAMERA_SESSION_CONFIGURATION_UTILS_H
+#define ANDROID_SERVERS_CAMERA_SESSION_CONFIGURATION_UTILS_H
+
+#include <android/hardware/camera2/BnCameraDeviceUser.h>
+#include <android/hardware/camera2/ICameraDeviceCallbacks.h>
+#include <camera/camera2/OutputConfiguration.h>
+#include <camera/camera2/SessionConfiguration.h>
+#include <camera/camera2/SubmitInfo.h>
+#include <android/hardware/camera/device/3.4/ICameraDeviceSession.h>
+
+#include <stdint.h>
+
+namespace android {
+
+typedef std::function<CameraMetadata (const String8 &)> metadataGetter;
+
+class SessionConfigurationUtils {
+public:
+ // utility function to convert AIDL SessionConfiguration to HIDL
+ // streamConfiguration. Also checks for sanity of SessionConfiguration and
+ // returns a non-ok binder::Status if the passed in session configuration
+ // isn't valid.
+ static binder::Status
+ convertToHALStreamCombination(const SessionConfiguration& sessionConfiguration,
+ const String8 &cameraId, const CameraMetadata &deviceInfo,
+ metadataGetter getMetadata, const std::vector<std::string> &physicalCameraIds,
+ hardware::camera::device::V3_4::StreamConfiguration &streamConfiguration,
+ bool *earlyExit);
+};
+
+} // android
+#endif
diff --git a/services/camera/libcameraservice/utils/TagMonitor.cpp b/services/camera/libcameraservice/utils/TagMonitor.cpp
index 4037a66..262f962 100644
--- a/services/camera/libcameraservice/utils/TagMonitor.cpp
+++ b/services/camera/libcameraservice/utils/TagMonitor.cpp
@@ -33,6 +33,16 @@
mVendorTagId(CAMERA_METADATA_INVALID_VENDOR_ID)
{}
+TagMonitor::TagMonitor(const TagMonitor& other):
+ mMonitoringEnabled(other.mMonitoringEnabled.load()),
+ mMonitoredTagList(other.mMonitoredTagList),
+ mLastMonitoredRequestValues(other.mLastMonitoredRequestValues),
+ mLastMonitoredResultValues(other.mLastMonitoredResultValues),
+ mLastMonitoredPhysicalRequestKeys(other.mLastMonitoredPhysicalRequestKeys),
+ mLastMonitoredPhysicalResultKeys(other.mLastMonitoredPhysicalResultKeys),
+ mMonitoringEvents(other.mMonitoringEvents),
+ mVendorTagId(other.mVendorTagId) {}
+
const String16 TagMonitor::kMonitorOption = String16("-m");
const char* TagMonitor::k3aTags =
diff --git a/services/camera/libcameraservice/utils/TagMonitor.h b/services/camera/libcameraservice/utils/TagMonitor.h
index 1b7b033..413f502 100644
--- a/services/camera/libcameraservice/utils/TagMonitor.h
+++ b/services/camera/libcameraservice/utils/TagMonitor.h
@@ -50,6 +50,8 @@
TagMonitor();
+ TagMonitor(const TagMonitor& other);
+
void initialize(metadata_vendor_id_t id) { mVendorTagId = id; }
// Parse tag name list (comma-separated) and if valid, enable monitoring
diff --git a/services/mediacodec/Android.bp b/services/mediacodec/Android.bp
index 3141c31..5811068 100644
--- a/services/mediacodec/Android.bp
+++ b/services/mediacodec/Android.bp
@@ -15,10 +15,6 @@
],
target: {
- vendor: {
- exclude_shared_libs: ["libavservices_minijail"],
- shared_libs: ["libavservices_minijail_vendor"],
- },
android: {
product_variables: {
malloc_not_svelte: {
diff --git a/services/mediacodec/Android.mk b/services/mediacodec/Android.mk
index 1cf0534..88a79e7 100644
--- a/services/mediacodec/Android.mk
+++ b/services/mediacodec/Android.mk
@@ -37,7 +37,7 @@
libutils \
liblog \
libbase \
- libavservices_minijail_vendor \
+ libavservices_minijail \
libcutils \
libhidlbase \
libstagefright_omx \
diff --git a/services/mediaextractor/Android.bp b/services/mediaextractor/Android.bp
index 816a6b1..9d6d169 100644
--- a/services/mediaextractor/Android.bp
+++ b/services/mediaextractor/Android.bp
@@ -15,6 +15,9 @@
"libutils",
"liblog",
],
+ header_libs: [
+ "libmediametrics_headers",
+ ],
}
// service executable
diff --git a/services/mediaextractor/MediaExtractorService.cpp b/services/mediaextractor/MediaExtractorService.cpp
index 9e814d1..a6cd224 100644
--- a/services/mediaextractor/MediaExtractorService.cpp
+++ b/services/mediaextractor/MediaExtractorService.cpp
@@ -61,13 +61,11 @@
}
::android::binder::Status MediaExtractorService::makeIDataSource(
- const base::unique_fd &fd,
+ base::unique_fd fd,
int64_t offset,
int64_t length,
::android::sp<::android::IDataSource>* _aidl_return) {
- // the caller will close the fd owned by the unique_fd upon return of this function,
- // so we need to dup() it to retain it.
- sp<DataSource> source = DataSourceFactory::getInstance()->CreateFromFd(dup(fd.get()), offset, length);
+ sp<DataSource> source = DataSourceFactory::getInstance()->CreateFromFd(fd.release(), offset, length);
*_aidl_return = CreateIDataSourceFromDataSource(source);
return binder::Status::ok();
}
diff --git a/services/mediaextractor/MediaExtractorService.h b/services/mediaextractor/MediaExtractorService.h
index 1b61c4b..1b40bf9 100644
--- a/services/mediaextractor/MediaExtractorService.h
+++ b/services/mediaextractor/MediaExtractorService.h
@@ -37,7 +37,7 @@
::android::sp<::android::IMediaExtractor>* _aidl_return);
virtual ::android::binder::Status makeIDataSource(
- const base::unique_fd &fd,
+ base::unique_fd fd,
int64_t offset,
int64_t length,
::android::sp<::android::IDataSource>* _aidl_return);
diff --git a/services/mediametrics/AnalyticsActions.h b/services/mediametrics/AnalyticsActions.h
new file mode 100644
index 0000000..5568c91
--- /dev/null
+++ b/services/mediametrics/AnalyticsActions.h
@@ -0,0 +1,150 @@
+/*
+ * 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 <media/MediaMetricsItem.h>
+#include <mutex>
+
+namespace android::mediametrics {
+
+/**
+ * AnalyticsActions consists of a map of pairs <trigger, action> which
+ * are evaluated for a given incoming MediaMetrics item.
+ *
+ * A vector of Actions are returned from getActionsForItem() which
+ * should be executed outside of any locks.
+ *
+ * Mediametrics assumes weak consistency, which is fine as the analytics database
+ * is generally strictly increasing in size (until gc removes values that are
+ * supposedly no longer needed).
+ */
+
+class AnalyticsActions {
+public:
+
+ using Elem = mediametrics::Item::Prop::Elem;
+ /**
+ * Trigger: a pair consisting of
+ * std::string: A wildcard url specifying a property in the item,
+ * where '*' indicates 0 or more arbitrary characters
+ * for the item key match.
+ * Elem: A value that needs to match exactly.
+ *
+ * Trigger is used in a map sort; default less with std::string as primary key.
+ * The wildcard accepts a string with '*' as being 0 or more arbitrary
+ * characters for the item key match. A wildcard is preferred over general
+ * regexp for simple fast lookup.
+ *
+ * TODO: incorporate a regexp option.
+ */
+ using Trigger = std::pair<std::string, Elem>;
+
+ /**
+ * Function: The function to be executed.
+ */
+ using Function = std::function<
+ void(const std::shared_ptr<const mediametrics::Item>& item)>;
+
+ /**
+ * Action: An action to execute. This is a shared pointer to Function.
+ */
+ using Action = std::shared_ptr<Function>;
+
+ /**
+ * Adds a new action.
+ *
+ * \param url references a property in the item with wildcards
+ * \param value references a value (cast to Elem automatically)
+ * so be careful of the type. It must be one of
+ * the types acceptable to Elem.
+ * \param action is a function or lambda to execute if the url matches value
+ * in the item.
+ */
+ 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);
+ }
+
+ // TODO: remove an action.
+
+ /**
+ * Get all the actions triggered for a particular item.
+ *
+ * \param item to be analyzed for actions.
+ */
+ std::vector<Action>
+ getActionsForItem(const std::shared_ptr<const mediametrics::Item>& item) {
+ 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)) {
+ 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.
+ }
+ return actions;
+ }
+
+private:
+
+ static inline bool isMatch(const Trigger& trigger,
+ const std::shared_ptr<const mediametrics::Item>& item) {
+ const auto& [key, elem] = trigger;
+ if (!startsWith(key, item->getKey())) return false;
+ // The trigger key is in format (item key).propName, so + 1 skips '.' delimeter.
+ const char *propName = key.c_str() + item->getKey().size() + 1;
+ return item->hasPropElem(propName, elem);
+ }
+
+ static inline int isWildcardMatch(const Trigger& trigger,
+ const std::shared_ptr<const mediametrics::Item>& item) {
+ const auto& [key, elem] = trigger;
+ return item->recursiveWildcardCheckElem(key.c_str(), elem);
+ }
+
+ mutable std::mutex mLock;
+ std::map<Trigger, Action> mFilters; // GUARDED_BY mLock
+};
+
+} // namespace android::mediametrics
diff --git a/services/mediametrics/AnalyticsState.h b/services/mediametrics/AnalyticsState.h
new file mode 100644
index 0000000..290ed21
--- /dev/null
+++ b/services/mediametrics/AnalyticsState.h
@@ -0,0 +1,131 @@
+/*
+ * 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 "TimeMachine.h"
+#include "TransactionLog.h"
+
+namespace android::mediametrics {
+
+/**
+ * AnalyticsState consists of a TimeMachine and TransactionLog for a set
+ * of MediaMetrics Items.
+ *
+ * One can add new Items with the submit() method.
+ *
+ * The AnalyticsState may be cleared or duplicated to preserve state after crashes
+ * in services are detected.
+ *
+ * As its members may not be moveable due to mutexes, we use this encapsulation
+ * with a shared pointer in order to save it or duplicate it.
+ */
+class AnalyticsState {
+public:
+ /**
+ * Returns success if AnalyticsState accepts the item.
+ *
+ * A trusted source can create a new key, an untrusted source
+ * can only modify the key if the uid will match that authorized
+ * on the existing key.
+ *
+ * \param item the item to be submitted.
+ * \param isTrusted whether the transaction comes from a trusted source.
+ * In this case, a trusted source is verified by binder
+ * UID to be a system service by MediaMetrics service.
+ * Do not use true if you haven't really checked!
+ *
+ * \return NO_ERROR on success or
+ * PERMISSION_DENIED if the item cannot be put into the AnalyticsState.
+ */
+ status_t submit(const std::shared_ptr<const mediametrics::Item>& item, bool isTrusted) {
+ return mTimeMachine.put(item, isTrusted) ?: mTransactionLog.put(item);
+ }
+
+ /**
+ * Returns the TimeMachine.
+ *
+ * The TimeMachine object is internally locked, so access is safe and defined,
+ * but multiple threaded access may change results after calling.
+ */
+ TimeMachine& timeMachine() { return mTimeMachine; }
+ const TimeMachine& timeMachine() const { return mTimeMachine; }
+
+ /**
+ * Returns the TransactionLog.
+ *
+ * The TransactionLog object is internally locked, so access is safe and defined,
+ * but multiple threaded access may change results after calling.
+ */
+ TransactionLog& transactionLog() { return mTransactionLog; }
+ const TransactionLog& transactionLog() const { return mTransactionLog; }
+
+ /**
+ * 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.
+ *
+ * The TimeMachine and the TransactionLog are dumped separately under
+ * different locks, so may not be 100% consistent with the last data
+ * delivered.
+ *
+ * \param lines the maximum number of lines in the string returned.
+ */
+ std::pair<std::string, int32_t> dump(int32_t lines = INT32_MAX) const {
+ std::stringstream ss;
+ int32_t ll = lines;
+
+ if (ll > 0) {
+ ss << "TransactionLog:\n";
+ --ll;
+ }
+ if (ll > 0) {
+ auto [s, l] = mTransactionLog.dump(ll);
+ ss << s;
+ ll -= l;
+ }
+ if (ll > 0) {
+ ss << "TimeMachine:\n";
+ --ll;
+ }
+ if (ll > 0) {
+ auto [s, l] = mTimeMachine.dump(ll);
+ ss << s;
+ ll -= l;
+ }
+ return { ss.str(), lines - ll };
+ }
+
+ /**
+ * Clears the AnalyticsState.
+ */
+ void clear() {
+ mTimeMachine.clear();
+ mTransactionLog.clear();
+ }
+
+private:
+ // Note: TimeMachine and TransactionLog are individually locked.
+ // Access to these objects under multiple threads will be weakly synchronized,
+ // which is acceptable as modifications only increase the history (or with GC,
+ // eliminates very old history).
+
+ TimeMachine mTimeMachine;
+ TransactionLog mTransactionLog;
+};
+
+} // namespace android::mediametrics
diff --git a/services/mediametrics/Android.bp b/services/mediametrics/Android.bp
index 0840f31..ec59ec1 100644
--- a/services/mediametrics/Android.bp
+++ b/services/mediametrics/Android.bp
@@ -12,8 +12,12 @@
"libbinder",
"liblog",
"libmediametricsservice",
+ "libmediautils",
"libutils",
],
+ header_libs: [
+ "libmediametrics_headers",
+ ],
init_rc: [
"mediametrics.rc",
@@ -52,6 +56,7 @@
"libbinder",
"liblog",
"libmediametrics",
+ "libmediautils",
"libprotobuf-cpp-lite",
"libstatslog",
"libutils",
diff --git a/services/mediametrics/AudioAnalytics.cpp b/services/mediametrics/AudioAnalytics.cpp
index 638c4ab..126e501 100644
--- a/services/mediametrics/AudioAnalytics.cpp
+++ b/services/mediametrics/AudioAnalytics.cpp
@@ -27,6 +27,66 @@
AudioAnalytics::AudioAnalytics()
{
ALOGD("%s", __func__);
+
+ // Add action to save AnalyticsState if audioserver is restarted.
+ // This triggers on an item of "audio.flinger"
+ // with a property "event" set to "AudioFlinger" (the constructor).
+ mActions.addAction(
+ AMEDIAMETRICS_KEY_AUDIO_FLINGER "." AMEDIAMETRICS_PROP_EVENT,
+ std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_CTOR),
+ std::make_shared<AnalyticsActions::Function>(
+ [this](const std::shared_ptr<const android::mediametrics::Item> &item){
+ ALOGW("(key=%s) Audioflinger constructor event detected", item->getKey().c_str());
+ mPreviousAnalyticsState.set(std::make_shared<AnalyticsState>(
+ *mAnalyticsState.get()));
+ // Note: get returns shared_ptr temp, whose lifetime is extended
+ // to end of full expression.
+ mAnalyticsState->clear(); // TODO: filter the analytics state.
+ // Perhaps report this.
+ }));
+
+ // Check underruns
+ mActions.addAction(
+ AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD "*." AMEDIAMETRICS_PROP_EVENT,
+ std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_UNDERRUN),
+ 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
+ }
+ }));
+
+ // Check latencies, playback and startup
+ mActions.addAction(
+ AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK "*." AMEDIAMETRICS_PROP_LATENCYMS,
+ std::monostate{}, // accept any value
+ 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;
+
+ 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
+ }
+ }));
}
AudioAnalytics::~AudioAnalytics()
@@ -35,13 +95,16 @@
}
status_t AudioAnalytics::submit(
- const std::shared_ptr<const MediaAnalyticsItem>& item, bool isTrusted)
+ const std::shared_ptr<const mediametrics::Item>& item, bool isTrusted)
{
- if (startsWith(item->getKey(), "audio.")) {
- return mTimeMachine.put(item, isTrusted)
- ?: mTransactionLog.put(item);
- }
- return BAD_VALUE;
+ if (!startsWith(item->getKey(), AMEDIAMETRICS_KEY_PREFIX_AUDIO)) return BAD_VALUE;
+ status_t status = mAnalyticsState->submit(item, isTrusted);
+ if (status != NO_ERROR) return status; // may not be permitted.
+
+ // Only if the item was successfully submitted (permission)
+ // do we check triggered actions.
+ checkActions(item);
+ return NO_ERROR;
}
std::pair<std::string, int32_t> AudioAnalytics::dump(int32_t lines) const
@@ -50,24 +113,41 @@
int32_t ll = lines;
if (ll > 0) {
- ss << "TransactionLog:\n";
- --ll;
- }
- if (ll > 0) {
- auto [s, l] = mTransactionLog.dump(ll);
+ auto [s, l] = mAnalyticsState->dump(ll);
ss << s;
ll -= l;
}
if (ll > 0) {
- ss << "TimeMachine:\n";
+ ss << "Prior audioserver state:\n";
--ll;
}
if (ll > 0) {
- auto [s, l] = mTimeMachine.dump(ll);
+ auto [s, l] = mPreviousAnalyticsState->dump(ll);
ss << s;
ll -= l;
}
return { ss.str(), lines - ll };
}
+void AudioAnalytics::checkActions(const std::shared_ptr<const mediametrics::Item>& item)
+{
+ auto actions = mActions.getActionsForItem(item); // internally locked.
+ // Execute actions with no lock held.
+ for (const auto& action : actions) {
+ (*action)(item);
+ }
+}
+
+// HELPER METHODS
+
+std::string AudioAnalytics::getThreadFromTrack(const std::string& track) const
+{
+ int32_t threadId_int32{};
+ if (mAnalyticsState->timeMachine().get(
+ track, AMEDIAMETRICS_PROP_THREADID, &threadId_int32) != NO_ERROR) {
+ return {};
+ }
+ return std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD) + std::to_string(threadId_int32);
+}
+
} // namespace android
diff --git a/services/mediametrics/AudioAnalytics.h b/services/mediametrics/AudioAnalytics.h
index 366a809..4a42e22 100644
--- a/services/mediametrics/AudioAnalytics.h
+++ b/services/mediametrics/AudioAnalytics.h
@@ -16,8 +16,9 @@
#pragma once
-#include "TimeMachine.h"
-#include "TransactionLog.h"
+#include "AnalyticsActions.h"
+#include "AnalyticsState.h"
+#include "Wrap.h"
namespace android::mediametrics {
@@ -27,7 +28,6 @@
AudioAnalytics();
~AudioAnalytics();
- // TODO: update with conditions for keys.
/**
* Returns success if AudioAnalytics recognizes item.
*
@@ -42,8 +42,12 @@
* In this case, a trusted source is verified by binder
* UID to be a system service by MediaMetrics service.
* Do not use true if you haven't really checked!
+ *
+ * \return NO_ERROR on success,
+ * PERMISSION_DENIED if the item cannot be put into the AnalyticsState,
+ * BAD_VALUE if the item key does not start with "audio.".
*/
- status_t submit(const std::shared_ptr<const MediaAnalyticsItem>& item, bool isTrusted);
+ status_t submit(const std::shared_ptr<const mediametrics::Item>& item, bool isTrusted);
/**
* Returns a pair consisting of the dump string, and the number of lines in the string.
@@ -60,9 +64,30 @@
std::pair<std::string, int32_t> dump(int32_t lines = INT32_MAX) const;
private:
- // The following are locked internally
- TimeMachine mTimeMachine;
- TransactionLog mTransactionLog;
+
+ /**
+ * Checks for any pending actions for a particular item.
+ *
+ * \param item to check against the current AnalyticsActions.
+ */
+ void checkActions(const std::shared_ptr<const mediametrics::Item>& item);
+
+ // HELPER METHODS
+ /**
+ * Return the audio thread associated with an audio track name.
+ * e.g. "audio.track.32" -> "audio.thread.10" if the associated
+ * threadId for the audio track is 10.
+ */
+ std::string getThreadFromTrack(const std::string& track) const;
+
+ // Actions is individually locked
+ AnalyticsActions mActions;
+
+ // AnalyticsState is individually locked, and we use SharedPtrWrap
+ // to allow safe access even if the shared pointer changes underneath.
+
+ SharedPtrWrap<AnalyticsState> mAnalyticsState;
+ SharedPtrWrap<AnalyticsState> mPreviousAnalyticsState;
};
} // namespace android::mediametrics
diff --git a/services/mediametrics/MediaMetricsService.cpp b/services/mediametrics/MediaMetricsService.cpp
index b735b81..4f8589c 100644
--- a/services/mediametrics/MediaMetricsService.cpp
+++ b/services/mediametrics/MediaMetricsService.cpp
@@ -15,7 +15,7 @@
*/
//#define LOG_NDEBUG 0
-#define LOG_TAG "MediaAnalyticsService"
+#define LOG_TAG "MediaMetricsService"
#include <utils/Log.h>
#include "MediaMetricsService.h"
@@ -52,21 +52,39 @@
// TODO: need to look at tuning kMaxRecords and friends for low-memory devices
/* static */
-nsecs_t MediaAnalyticsService::roundTime(nsecs_t timeNs)
+nsecs_t MediaMetricsService::roundTime(nsecs_t timeNs)
{
return (timeNs + NANOS_PER_SECOND / 2) / NANOS_PER_SECOND * NANOS_PER_SECOND;
}
-MediaAnalyticsService::MediaAnalyticsService()
+/* static */
+bool MediaMetricsService::useUidForPackage(
+ const std::string& package, const std::string& installer)
+{
+ if (strchr(package.c_str(), '.') == NULL) {
+ return false; // not of form 'com.whatever...'; assume internal and ok
+ } else if (strncmp(package.c_str(), "android.", 8) == 0) {
+ return false; // android.* packages are assumed fine
+ } else if (strncmp(installer.c_str(), "com.android.", 12) == 0) {
+ return false; // from play store
+ } else if (strncmp(installer.c_str(), "com.google.", 11) == 0) {
+ return false; // some google source
+ } else if (strcmp(installer.c_str(), "preload") == 0) {
+ return false; // preloads
+ } else {
+ return true; // we're not sure where it came from, use uid only.
+ }
+}
+
+MediaMetricsService::MediaMetricsService()
: mMaxRecords(kMaxRecords),
mMaxRecordAgeNs(kMaxRecordAgeNs),
- mMaxRecordsExpiredAtOnce(kMaxExpiredAtOnce),
- mDumpProtoDefault(MediaAnalyticsItem::PROTO_V1)
+ mMaxRecordsExpiredAtOnce(kMaxExpiredAtOnce)
{
ALOGD("%s", __func__);
}
-MediaAnalyticsService::~MediaAnalyticsService()
+MediaMetricsService::~MediaMetricsService()
{
ALOGD("%s", __func__);
// the class destructor clears anyhow, but we enforce clearing items first.
@@ -74,7 +92,7 @@
mItems.clear();
}
-status_t MediaAnalyticsService::submitInternal(MediaAnalyticsItem *item, bool release)
+status_t MediaMetricsService::submitInternal(mediametrics::Item *item, bool release)
{
// calling PID is 0 for one-way calls.
const pid_t pid = IPCThreadState::self()->getCallingPid();
@@ -112,14 +130,19 @@
break;
}
- // Overwrite package name and version if the caller was untrusted.
- if (!isTrusted) {
- mUidInfo.setPkgInfo(item, item->getUid(), true, true);
- } else if (item->getPkgName().empty()) {
- // empty, so fill out both parts
- mUidInfo.setPkgInfo(item, item->getUid(), true, true);
- } else {
- // trusted, provided a package, do nothing
+ // 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);
+ }
}
ALOGV("%s: given uid %d; sanitized uid: %d sanitized pkg: %s "
@@ -147,33 +170,33 @@
}
if (!isTrusted || item->getTimestamp() == 0) {
- // WestWorld logs two times for events: ElapsedRealTimeNs (BOOTTIME) and
- // WallClockTimeNs (REALTIME). The new audio keys use BOOTTIME.
+ // Westworld logs two times for events: ElapsedRealTimeNs (BOOTTIME) and
+ // WallClockTimeNs (REALTIME), but currently logs REALTIME to cloud.
//
- // TODO: Reevaluate time base with other teams.
- const bool useBootTime = startsWith(item->getKey(), "audio.");
- const int64_t now = systemTime(useBootTime ? SYSTEM_TIME_BOOTTIME : SYSTEM_TIME_REALTIME);
+ // For consistency and correlation with other logging mechanisms
+ // we use REALTIME here.
+ const int64_t now = systemTime(SYSTEM_TIME_REALTIME);
item->setTimestamp(now);
}
// now attach either the item or its dup to a const shared pointer
- std::shared_ptr<const MediaAnalyticsItem> sitem(release ? item : item->dup());
+ std::shared_ptr<const mediametrics::Item> sitem(release ? item : item->dup());
(void)mAudioAnalytics.submit(sitem, isTrusted);
- extern bool dump2Statsd(const std::shared_ptr<const MediaAnalyticsItem>& item);
+ extern bool dump2Statsd(const std::shared_ptr<const mediametrics::Item>& item);
(void)dump2Statsd(sitem); // failure should be logged in function.
saveItem(sitem);
return NO_ERROR;
}
-status_t MediaAnalyticsService::dump(int fd, const Vector<String16>& args)
+status_t MediaMetricsService::dump(int fd, const Vector<String16>& args)
{
String8 result;
if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
result.appendFormat("Permission Denial: "
- "can't dump MediaAnalyticsService from pid=%d, uid=%d\n",
+ "can't dump MediaMetricsService from pid=%d, uid=%d\n",
IPCThreadState::self()->getCallingPid(),
IPCThreadState::self()->getCallingUid());
write(fd, result.string(), result.size());
@@ -182,7 +205,6 @@
// crack any parameters
const String16 protoOption("-proto");
- int chosenProto = mDumpProtoDefault;
const String16 clearOption("-clear");
bool clear = false;
const String16 sinceOption("-since");
@@ -197,21 +219,7 @@
} else if (args[i] == protoOption) {
i++;
if (i < n) {
- String8 value(args[i]);
- int proto = MediaAnalyticsItem::PROTO_V0;
- char *endp;
- const char *p = value.string();
- proto = strtol(p, &endp, 10);
- if (endp != p || *endp == '\0') {
- if (proto < MediaAnalyticsItem::PROTO_FIRST) {
- proto = MediaAnalyticsItem::PROTO_FIRST;
- } else if (proto > MediaAnalyticsItem::PROTO_LAST) {
- proto = MediaAnalyticsItem::PROTO_LAST;
- }
- chosenProto = proto;
- } else {
- result.append("unable to parse value for -proto\n\n");
- }
+ // ignore
} else {
result.append("missing value for -proto\n\n");
}
@@ -257,8 +265,8 @@
std::lock_guard _l(mLock);
result.appendFormat("Dump of the %s process:\n", kServiceName);
- dumpHeaders_l(result, chosenProto, ts_since);
- dumpRecent_l(result, chosenProto, ts_since, only.c_str());
+ dumpHeaders_l(result, ts_since);
+ dumpRecent_l(result, ts_since, only.c_str());
if (clear) {
mItemsDiscarded += mItems.size();
@@ -267,7 +275,11 @@
}
// TODO: maybe consider a better way of dumping audio analytics info.
constexpr int32_t linesToDump = 1000;
- result.append(mAudioAnalytics.dump(linesToDump).first.c_str());
+ auto [ dumpString, lines ] = mAudioAnalytics.dump(linesToDump);
+ result.append(dumpString.c_str());
+ if (lines == linesToDump) {
+ result.append("-- some lines may be truncated --\n");
+ }
}
write(fd, result.string(), result.size());
@@ -275,10 +287,9 @@
}
// dump headers
-void MediaAnalyticsService::dumpHeaders_l(String8 &result, int dumpProto, nsecs_t ts_since)
+void MediaMetricsService::dumpHeaders_l(String8 &result, nsecs_t ts_since)
{
- result.appendFormat("Protocol Version: %d\n", dumpProto);
- if (MediaAnalyticsItem::isEnabled()) {
+ if (mediametrics::Item::isEnabled()) {
result.append("Metrics gathering: enabled\n");
} else {
result.append("Metrics gathering: DISABLED via property\n");
@@ -297,26 +308,26 @@
}
}
-void MediaAnalyticsService::dumpRecent_l(
- String8 &result, int dumpProto, nsecs_t ts_since, const char * only)
+void MediaMetricsService::dumpRecent_l(
+ String8 &result, nsecs_t ts_since, const char * only)
{
if (only != nullptr && *only == '\0') {
only = nullptr;
}
result.append("\nFinalized Metrics (oldest first):\n");
- dumpQueue_l(result, dumpProto, ts_since, only);
+ dumpQueue_l(result, ts_since, only);
// show who is connected and injecting records?
// talk about # records fed to the 'readers'
// talk about # records we discarded, perhaps "discarded w/o reading" too
}
-void MediaAnalyticsService::dumpQueue_l(String8 &result, int dumpProto) {
- dumpQueue_l(result, dumpProto, (nsecs_t) 0, nullptr /* only */);
+void MediaMetricsService::dumpQueue_l(String8 &result) {
+ dumpQueue_l(result, (nsecs_t) 0, nullptr /* only */);
}
-void MediaAnalyticsService::dumpQueue_l(
- String8 &result, int dumpProto, nsecs_t ts_since, const char * only) {
+void MediaMetricsService::dumpQueue_l(
+ String8 &result, nsecs_t ts_since, const char * only) {
int slot = 0;
if (mItems.empty()) {
@@ -335,7 +346,7 @@
continue;
}
result.appendFormat("%5d: %s\n",
- slot, item->toString(dumpProto).c_str());
+ slot, item->toString().c_str());
slot++;
}
}
@@ -346,7 +357,7 @@
// if item != NULL, it's the item we just inserted
// true == more items eligible to be recovered
-bool MediaAnalyticsService::expirations_l(const std::shared_ptr<const MediaAnalyticsItem>& item)
+bool MediaMetricsService::expirations_l(const std::shared_ptr<const mediametrics::Item>& item)
{
bool more = false;
@@ -395,7 +406,7 @@
return more;
}
-void MediaAnalyticsService::processExpirations()
+void MediaMetricsService::processExpirations()
{
bool more;
do {
@@ -405,7 +416,7 @@
} while (more);
}
-void MediaAnalyticsService::saveItem(const std::shared_ptr<const MediaAnalyticsItem>& item)
+void MediaMetricsService::saveItem(const std::shared_ptr<const mediametrics::Item>& item)
{
std::lock_guard _l(mLock);
// we assume the items are roughly in time order.
@@ -419,7 +430,7 @@
}
/* static */
-bool MediaAnalyticsService::isContentValid(const MediaAnalyticsItem *item, bool isTrusted)
+bool MediaMetricsService::isContentValid(const mediametrics::Item *item, bool isTrusted)
{
if (isTrusted) return true;
// untrusted uids can only send us a limited set of keys
@@ -445,155 +456,9 @@
}
// are we rate limited, normally false
-bool MediaAnalyticsService::isRateLimited(MediaAnalyticsItem *) const
+bool MediaMetricsService::isRateLimited(mediametrics::Item *) const
{
return false;
}
-// How long we hold package info before we re-fetch it
-constexpr nsecs_t PKG_EXPIRATION_NS = 30 * 60 * NANOS_PER_SECOND; // 30 minutes
-
-// give me the package name, perhaps going to find it
-// manages its own mutex operations internally
-void MediaAnalyticsService::UidInfo::setPkgInfo(
- MediaAnalyticsItem *item, uid_t uid, bool setName, bool setVersion)
-{
- ALOGV("%s: uid=%d", __func__, uid);
-
- if (!setName && !setVersion) {
- return; // setting nothing? strange
- }
-
- const nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
- struct UidToPkgInfo mapping;
- {
- std::lock_guard _l(mUidInfoLock);
- auto it = mPkgMappings.find(uid);
- if (it != mPkgMappings.end()) {
- mapping = it->second;
- ALOGV("%s: uid %d expiration %lld now %lld",
- __func__, uid, (long long)mapping.expiration, (long long)now);
- if (mapping.expiration <= now) {
- // purge the stale entry and fall into re-fetching
- ALOGV("%s: entry for uid %d expired, now %lld",
- __func__, uid, (long long)now);
- mPkgMappings.erase(it);
- mapping.uid = (uid_t)-1; // this is always fully overwritten
- }
- }
- }
-
- // if we did not find it
- if (mapping.uid == (uid_t)(-1)) {
- std::string pkg;
- std::string installer;
- int64_t versionCode = 0;
-
- const struct passwd *pw = getpwuid(uid);
- if (pw) {
- pkg = pw->pw_name;
- }
-
- sp<IServiceManager> sm = defaultServiceManager();
- sp<content::pm::IPackageManagerNative> package_mgr;
- if (sm.get() == nullptr) {
- ALOGE("%s: Cannot find service manager", __func__);
- } else {
- sp<IBinder> binder = sm->getService(String16("package_native"));
- if (binder.get() == nullptr) {
- ALOGE("%s: Cannot find package_native", __func__);
- } else {
- package_mgr = interface_cast<content::pm::IPackageManagerNative>(binder);
- }
- }
-
- if (package_mgr != nullptr) {
- std::vector<int> uids;
- std::vector<std::string> names;
- uids.push_back(uid);
- binder::Status status = package_mgr->getNamesForUids(uids, &names);
- if (!status.isOk()) {
- ALOGE("%s: getNamesForUids failed: %s",
- __func__, status.exceptionMessage().c_str());
- } else {
- if (!names[0].empty()) {
- pkg = names[0].c_str();
- }
- }
- }
-
- // strip any leading "shared:" strings that came back
- if (pkg.compare(0, 7, "shared:") == 0) {
- pkg.erase(0, 7);
- }
- // determine how pkg was installed and the versionCode
- if (pkg.empty()) {
- pkg = std::to_string(uid); // no name for us to manage
- } else if (strchr(pkg.c_str(), '.') == NULL) {
- // not of form 'com.whatever...'; assume internal and ok
- } else if (strncmp(pkg.c_str(), "android.", 8) == 0) {
- // android.* packages are assumed fine
- } else if (package_mgr.get() != nullptr) {
- String16 pkgName16(pkg.c_str());
- binder::Status status = package_mgr->getInstallerForPackage(pkgName16, &installer);
- if (!status.isOk()) {
- ALOGE("%s: getInstallerForPackage failed: %s",
- __func__, status.exceptionMessage().c_str());
- }
-
- // skip if we didn't get an installer
- if (status.isOk()) {
- status = package_mgr->getVersionCodeForPackage(pkgName16, &versionCode);
- if (!status.isOk()) {
- ALOGE("%s: getVersionCodeForPackage failed: %s",
- __func__, status.exceptionMessage().c_str());
- }
- }
-
- ALOGV("%s: package '%s' installed by '%s' versioncode %lld",
- __func__, pkg.c_str(), installer.c_str(), (long long)versionCode);
-
- if (strncmp(installer.c_str(), "com.android.", 12) == 0) {
- // from play store, we keep info
- } else if (strncmp(installer.c_str(), "com.google.", 11) == 0) {
- // some google source, we keep info
- } else if (strcmp(installer.c_str(), "preload") == 0) {
- // preloads, we keep the info
- } else if (installer.c_str()[0] == '\0') {
- // sideload (no installer); report UID only
- pkg = std::to_string(uid);
- versionCode = 0;
- } else {
- // unknown installer; report UID only
- pkg = std::to_string(uid);
- versionCode = 0;
- }
- } else {
- // unvalidated by package_mgr just send uid.
- pkg = std::to_string(uid);
- }
-
- // add it to the map, to save a subsequent lookup
- std::lock_guard _l(mUidInfoLock);
- // always overwrite
- mapping.uid = uid;
- mapping.pkg = std::move(pkg);
- mapping.installer = std::move(installer);
- mapping.versionCode = versionCode;
- mapping.expiration = now + PKG_EXPIRATION_NS;
- ALOGV("%s: adding uid %d pkg '%s' expiration: %lld",
- __func__, uid, mapping.pkg.c_str(), (long long)mapping.expiration);
- mPkgMappings[uid] = mapping;
- }
-
- if (mapping.uid != (uid_t)(-1)) {
- if (setName) {
- item->setPkgName(mapping.pkg);
- }
- if (setVersion) {
- item->setPkgVersionCode(mapping.versionCode);
- }
- }
-}
-
} // namespace android
diff --git a/services/mediametrics/MediaMetricsService.h b/services/mediametrics/MediaMetricsService.h
index 5bdc48f..935bee2 100644
--- a/services/mediametrics/MediaMetricsService.h
+++ b/services/mediametrics/MediaMetricsService.h
@@ -22,19 +22,20 @@
#include <mutex>
#include <unordered_map>
-// IMediaAnalyticsService must include Vector, String16, Errors
-#include <media/IMediaAnalyticsService.h>
+// IMediaMetricsService must include Vector, String16, Errors
+#include <media/IMediaMetricsService.h>
+#include <mediautils/ServiceUtilities.h>
#include <utils/String8.h>
#include "AudioAnalytics.h"
namespace android {
-class MediaAnalyticsService : public BnMediaAnalyticsService
+class MediaMetricsService : public BnMediaMetricsService
{
public:
- MediaAnalyticsService();
- ~MediaAnalyticsService() override;
+ MediaMetricsService();
+ ~MediaMetricsService() override;
/**
* Submits the indicated record to the mediaanalytics service.
@@ -43,12 +44,12 @@
* \return status failure, which is negative on binder transaction failure.
* As the transaction is one-way, remote failures will not be reported.
*/
- status_t submit(MediaAnalyticsItem *item) override {
+ status_t submit(mediametrics::Item *item) override {
return submitInternal(item, false /* release */);
}
status_t submitBuffer(const char *buffer, size_t length) override {
- MediaAnalyticsItem *item = new MediaAnalyticsItem();
+ mediametrics::Item *item = new mediametrics::Item();
return item->readFromByteString(buffer, length)
?: submitInternal(item, true /* release */);
}
@@ -62,28 +63,33 @@
*/
static nsecs_t roundTime(nsecs_t timeNs);
+ /**
+ * Returns true if we should use uid for package name when uploading to WestWorld.
+ */
+ static bool useUidForPackage(const std::string& package, const std::string& installer);
+
protected:
// Internal call where release is true if ownership of item is transferred
// to the service (that is, the service will eventually delete the item).
- status_t submitInternal(MediaAnalyticsItem *item, bool release) override;
+ status_t submitInternal(mediametrics::Item *item, bool release) override;
private:
void processExpirations();
// input validation after arrival from client
- static bool isContentValid(const MediaAnalyticsItem *item, bool isTrusted);
- bool isRateLimited(MediaAnalyticsItem *) const;
- void saveItem(const std::shared_ptr<const MediaAnalyticsItem>& item);
+ static bool isContentValid(const mediametrics::Item *item, bool isTrusted);
+ bool isRateLimited(mediametrics::Item *) const;
+ void saveItem(const std::shared_ptr<const mediametrics::Item>& item);
// The following methods are GUARDED_BY(mLock)
- bool expirations_l(const std::shared_ptr<const MediaAnalyticsItem>& item);
+ bool expirations_l(const std::shared_ptr<const mediametrics::Item>& item);
// support for generating output
- void dumpQueue_l(String8 &result, int dumpProto);
- void dumpQueue_l(String8 &result, int dumpProto, nsecs_t, const char *only);
- void dumpHeaders_l(String8 &result, int dumpProto, nsecs_t ts_since);
- void dumpSummaries_l(String8 &result, int dumpProto, nsecs_t ts_since, const char * only);
- void dumpRecent_l(String8 &result, int dumpProto, nsecs_t ts_since, const char * only);
+ void dumpQueue_l(String8 &result);
+ void dumpQueue_l(String8 &result, nsecs_t, const char *only);
+ void dumpHeaders_l(String8 &result, nsecs_t ts_since);
+ void dumpSummaries_l(String8 &result, nsecs_t ts_since, const char * only);
+ void dumpRecent_l(String8 &result, nsecs_t ts_since, const char * only);
// The following variables accessed without mLock
@@ -94,29 +100,11 @@
const nsecs_t mMaxRecordAgeNs;
// max to expire per expirations_l() invocation
const size_t mMaxRecordsExpiredAtOnce;
- const int mDumpProtoDefault;
-
- class UidInfo {
- public:
- void setPkgInfo(MediaAnalyticsItem *item, uid_t uid, bool setName, bool setVersion);
-
- private:
- std::mutex mUidInfoLock;
-
- struct UidToPkgInfo {
- uid_t uid = -1;
- std::string pkg;
- std::string installer;
- int64_t versionCode = 0;
- nsecs_t expiration = 0; // TODO: remove expiration.
- };
-
- // TODO: use concurrent hashmap with striped lock.
- std::unordered_map<uid_t, struct UidToPkgInfo> mPkgMappings; // GUARDED_BY(mUidInfoLock)
- } mUidInfo; // mUidInfo can be accessed without lock (locked internally)
std::atomic<int64_t> mItemsSubmitted{}; // accessed outside of lock.
+ mediautils::UidInfo mUidInfo; // mUidInfo can be accessed without lock (locked internally)
+
mediametrics::AudioAnalytics mAudioAnalytics;
std::mutex mLock;
@@ -132,7 +120,7 @@
// Our item queue, generally (oldest at front)
// TODO: Make separate class, use segmented queue, write lock only end.
// Note: Another analytics module might have ownership of an item longer than the log.
- std::deque<std::shared_ptr<const MediaAnalyticsItem>> mItems; // GUARDED_BY(mLock)
+ std::deque<std::shared_ptr<const mediametrics::Item>> mItems; // GUARDED_BY(mLock)
};
} // namespace android
diff --git a/services/mediametrics/TimeMachine.h b/services/mediametrics/TimeMachine.h
index 578b838..87de1c4 100644
--- a/services/mediametrics/TimeMachine.h
+++ b/services/mediametrics/TimeMachine.h
@@ -23,7 +23,7 @@
#include <variant>
#include <vector>
-#include <media/MediaAnalyticsItem.h>
+#include <media/MediaMetricsItem.h>
#include <utils/Timers.h>
namespace android::mediametrics {
@@ -35,6 +35,14 @@
return s;
}
+// define a way of printing a std::pair.
+template <typename T, typename U>
+std::ostream & operator<< (std::ostream& s,
+ const std::pair<T, U>& v) {
+ s << "{ " << v.first << ", " << v.second << " }";
+ return s;
+}
+
// define a way of printing a variant
// see https://en.cppreference.com/w/cpp/utility/variant/visit
template <typename T0, typename ... Ts>
@@ -52,11 +60,13 @@
*
* The TimeMachine is NOT thread safe.
*/
-class TimeMachine {
-
- using Elem = std::variant<std::monostate, int32_t, int64_t, double, std::string>;
+class TimeMachine final { // made final as we have copy constructor instead of dup() override.
+public:
+ using Elem = Item::Prop::Elem; // use the Item property element.
using PropertyHistory = std::multimap<int64_t /* time */, Elem>;
+private:
+
// KeyHistory contains no lock.
// Access is through the TimeMachine, and a hash-striped lock is used
// before calling into KeyHistory.
@@ -70,10 +80,12 @@
, mCreationTime(time)
, mLastModificationTime(time)
{
- putValue("_pid", (int32_t)pid, time);
- putValue("_uid", (int32_t)uid, time);
+ putValue(BUNDLE_PID, (int32_t)pid, time);
+ putValue(BUNDLE_UID, (int32_t)uid, time);
}
+ KeyHistory(const KeyHistory &other) = default;
+
status_t checkPermission(uid_t uidCheck) const {
return uidCheck != (uid_t)-1 && uidCheck != mUid ? PERMISSION_DENIED : NO_ERROR;
}
@@ -101,8 +113,9 @@
}
void putProp(
- const std::string &name, const MediaAnalyticsItem::Prop &prop, int64_t time = 0) {
- prop.visit([&](auto value) { putValue(name, value, time); });
+ const std::string &name, const mediametrics::Item::Prop &prop, int64_t time = 0) {
+ //alternatively: prop.visit([&](auto value) { putValue(name, value, time); });
+ putValue(name, prop.get(), time);
}
template <typename T>
@@ -119,13 +132,6 @@
}
}
- // Explicitly ignore rate properties - we don't expose them for now.
- void putValue(
- const std::string &property __unused,
- std::pair<int64_t, int64_t>& e __unused,
- int64_t time __unused) {
- }
-
std::pair<std::string, int32_t> dump(int32_t lines, int64_t time) const {
std::stringstream ss;
int32_t ll = lines;
@@ -151,9 +157,22 @@
}
std::stringstream ss;
ss << key << "." << tsPair.first << "={";
- do {
- ss << eptr->first << ":" << eptr->second << ",";
- } while (++eptr != timeSequence.end());
+
+ time_string_t last_timestring{}; // last timestring used.
+ while (true) {
+ const time_string_t timestring = mediametrics::timeStringFromNs(eptr->first);
+ // find common prefix offset.
+ const size_t offset = commonTimePrefixPosition(timestring.time,
+ last_timestring.time);
+ last_timestring = timestring;
+ ss << "(" << (offset == 0 ? "" : "~") << ×tring.time[offset]
+ << ") " << eptr->second;
+ if (++eptr == timeSequence.end()) {
+ ss << "}";
+ break;
+ }
+ ss << ", ";
+ }
ss << "};\n";
return ss.str();
}
@@ -185,10 +204,38 @@
__func__, keyHighWaterMark, keyLowWaterMark);
}
+ // The TimeMachine copy constructor/assignment uses a deep copy,
+ // though the snapshot is not instantaneous nor isochronous.
+ //
+ // If there are concurrent operations ongoing in the other TimeMachine
+ // then there may be some history more recent than others (a time shear).
+ // This is expected to be a benign addition in history as small number of
+ // future elements are incorporated.
+ TimeMachine(const TimeMachine& other) {
+ *this = other;
+ }
+ TimeMachine& operator=(const TimeMachine& other) {
+ std::lock_guard lock(mLock);
+ mHistory.clear();
+
+ {
+ std::lock_guard lock2(other.mLock);
+ mHistory = other.mHistory;
+ }
+
+ // Now that we safely have our own shared pointers, let's dup them
+ // to ensure they are decoupled. We do this by acquiring the other lock.
+ for (const auto &[lkey, lhist] : mHistory) {
+ std::lock_guard lock2(other.getLockForKey(lkey));
+ mHistory[lkey] = std::make_shared<KeyHistory>(*lhist);
+ }
+ return *this;
+ }
+
/**
* Put all the properties from an item into the Time Machine log.
*/
- status_t put(const std::shared_ptr<const MediaAnalyticsItem>& item, bool isTrusted = false) {
+ status_t put(const std::shared_ptr<const mediametrics::Item>& item, bool isTrusted = false) {
const int64_t time = item->getTimestamp();
const std::string &key = item->getKey();
@@ -214,7 +261,7 @@
}
// deferred contains remote properties (for other keys) to do later.
- std::vector<const MediaAnalyticsItem::Prop *> deferred;
+ std::vector<const mediametrics::Item::Prop *> deferred;
{
// handle local properties
std::lock_guard lock(getLockForKey(key));
@@ -358,10 +405,10 @@
std::stringstream ss;
int32_t ll = lines;
- for (const auto &keyPair : mHistory) {
- std::lock_guard lock(getLockForKey(keyPair.first));
+ for (const auto &[lkey, lhist] : mHistory) {
+ std::lock_guard lock(getLockForKey(lkey));
if (lines <= 0) break;
- auto [s, l] = keyPair.second->dump(ll, time);
+ auto [s, l] = lhist->dump(ll, time);
ss << s;
ll -= l;
}
diff --git a/services/mediametrics/TransactionLog.h b/services/mediametrics/TransactionLog.h
index ca37862..27e2c14 100644
--- a/services/mediametrics/TransactionLog.h
+++ b/services/mediametrics/TransactionLog.h
@@ -21,12 +21,12 @@
#include <sstream>
#include <string>
-#include <media/MediaAnalyticsItem.h>
+#include <media/MediaMetricsItem.h>
namespace android::mediametrics {
/**
- * The TransactionLog is used to record MediaAnalyticsItems to present
+ * The TransactionLog is used to record mediametrics::Items to present
* different views on the time information (selected by audio, and sorted by key).
*
* The TransactionLog will always present data in timestamp order. (Perhaps we
@@ -36,7 +36,7 @@
*
* The TransactionLog is NOT thread safe.
*/
-class TransactionLog {
+class TransactionLog final { // made final as we have copy constructor instead of dup() override.
public:
// In long term run, the garbage collector aims to keep the
// Transaction Log between the Low Water Mark and the High Water Mark.
@@ -58,10 +58,34 @@
__func__, highWaterMark, lowWaterMark);
}
+ // The TransactionLog copy constructor/assignment is effectively an
+ // instantaneous, isochronous snapshot of the other TransactionLog.
+ //
+ // The contents of the Transaction Log are shared pointers to immutable instances -
+ // std::shared_ptr<const mediametrics::Item>, so we use a shallow copy,
+ // which is more efficient in space and execution time than a deep copy,
+ // and gives the same results.
+
+ TransactionLog(const TransactionLog &other) {
+ *this = other;
+ }
+
+ TransactionLog& operator=(const TransactionLog &other) {
+ std::lock_guard lock(mLock);
+ mLog.clear();
+ mItemMap.clear();
+
+ std::lock_guard lock2(other.mLock);
+ mLog = other.mLog;
+ mItemMap = other.mItemMap;
+
+ return *this;
+ }
+
/**
* Put an item in the TransactionLog.
*/
- status_t put(const std::shared_ptr<const MediaAnalyticsItem>& item) {
+ status_t put(const std::shared_ptr<const mediametrics::Item>& item) {
const std::string& key = item->getKey();
const int64_t time = item->getTimestamp();
@@ -77,7 +101,7 @@
/**
* Returns all records within [startTime, endTime]
*/
- std::vector<std::shared_ptr<const MediaAnalyticsItem>> get(
+ std::vector<std::shared_ptr<const mediametrics::Item>> get(
int64_t startTime = 0, int64_t endTime = INT64_MAX) const {
std::lock_guard lock(mLock);
return getItemsInRange_l(mLog, startTime, endTime);
@@ -86,7 +110,7 @@
/**
* Returns all records for a key within [startTime, endTime]
*/
- std::vector<std::shared_ptr<const MediaAnalyticsItem>> get(
+ std::vector<std::shared_ptr<const mediametrics::Item>> get(
const std::string& key,
int64_t startTime = 0, int64_t endTime = INT64_MAX) const {
std::lock_guard lock(mLock);
@@ -131,7 +155,7 @@
--ll;
for (const auto &item : itemMap.second) {
if (ll <= 0) break;
- ss << " { " << item.first << ", " << item.second->toString() << " }\n";
+ ss << " " << item.second->toString() << "\n";
--ll;
}
}
@@ -158,7 +182,7 @@
private:
using MapTimeItem =
- std::multimap<int64_t /* time */, std::shared_ptr<const MediaAnalyticsItem>>;
+ std::multimap<int64_t /* time */, std::shared_ptr<const mediametrics::Item>>;
// GUARDED_BY mLock
/**
@@ -179,7 +203,7 @@
// remove at least those elements.
// use a stale vector with precise type to avoid type erasure overhead in garbage
- std::vector<std::shared_ptr<const MediaAnalyticsItem>> stale;
+ std::vector<std::shared_ptr<const mediametrics::Item>> stale;
for (size_t i = 0; i < toRemove; ++i) {
stale.emplace_back(std::move(eraseEnd->second));
@@ -224,7 +248,7 @@
return true;
}
- static std::vector<std::shared_ptr<const MediaAnalyticsItem>> getItemsInRange_l(
+ static std::vector<std::shared_ptr<const mediametrics::Item>> getItemsInRange_l(
const MapTimeItem& map,
int64_t startTime = 0, int64_t endTime = INT64_MAX) {
auto it = map.lower_bound(startTime);
@@ -232,7 +256,7 @@
auto it2 = map.upper_bound(endTime);
- std::vector<std::shared_ptr<const MediaAnalyticsItem>> ret;
+ std::vector<std::shared_ptr<const mediametrics::Item>> ret;
while (it != it2) {
ret.push_back(it->second);
++it;
diff --git a/services/mediametrics/Wrap.h b/services/mediametrics/Wrap.h
new file mode 100644
index 0000000..3584e08
--- /dev/null
+++ b/services/mediametrics/Wrap.h
@@ -0,0 +1,205 @@
+/*
+ * 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 <memory>
+#include <mutex>
+
+namespace android::mediametrics {
+
+/**
+ * Wraps a shared-ptr for which member access through operator->() behaves
+ * as if the shared-ptr is atomically copied and then (without a lock) -> called.
+ *
+ * See related C++ 20:
+ * https://en.cppreference.com/w/cpp/memory/shared_ptr/atomic2
+ *
+ * EXAMPLE:
+ *
+ * SharedPtrWrap<T> t{};
+ *
+ * thread1() {
+ * t->func(); // safely executes either the original t or the one created by thread2.
+ * }
+ *
+ * thread2() {
+ * t.set(std::make_shared<T>()); // overwrites the original t.
+ * }
+ */
+template <typename T>
+class SharedPtrWrap {
+ mutable std::mutex mLock;
+ std::shared_ptr<T> mPtr;
+
+public:
+ template <typename... Args>
+ explicit SharedPtrWrap(Args&&... args)
+ : mPtr(std::make_shared<T>(std::forward<Args>(args)...))
+ {}
+
+ /**
+ * Gets the current shared pointer. This must return a value, not a reference.
+ *
+ * For compatibility with existing shared_ptr, we do not pass back a
+ * shared_ptr<const T> for the const getter.
+ */
+ std::shared_ptr<T> get() const {
+ std::lock_guard lock(mLock);
+ return mPtr;
+ }
+
+ /**
+ * Sets the current shared pointer, returning the previous shared pointer.
+ */
+ std::shared_ptr<T> set(std::shared_ptr<T> ptr) { // pass by value as we use swap.
+ std::lock_guard lock(mLock);
+ std::swap(ptr, mPtr);
+ return ptr;
+ }
+
+ /**
+ * Returns a shared pointer value representing T at the instant of time when
+ * the call executes. The lifetime of the shared pointer will
+ * be extended as we are returning an instance of the shared_ptr
+ * not a reference to it. The destructor to the returned shared_ptr
+ * will be called sometime after the expression including the member function or
+ * the member variable is evaluated. Do not change to a reference!
+ */
+
+ // For compatibility with existing shared_ptr, we do not pass back a
+ // shared_ptr<const T> for the const operator pointer access.
+ std::shared_ptr<T> operator->() const {
+ return get();
+ }
+ /**
+ * We do not overload operator*() as the reference is not stable if the
+ * lock is not held.
+ */
+};
+
+/**
+ * Wraps member access to the class T by a lock.
+ *
+ * The object T is constructed within the LockWrap to guarantee
+ * locked access at all times. When T's methods are accessed through ->,
+ * a monitor style lock is obtained to prevent multiple threads from executing
+ * methods in the object T at the same time.
+ * Suggested by Kevin R.
+ *
+ * EXAMPLE:
+ *
+ * // Accumulator class which is very slow, requires locking for multiple threads.
+ *
+ * class Accumulator {
+ * int32_t value_ = 0;
+ * public:
+ * void add(int32_t incr) {
+ * const int32_t temp = value_;
+ * sleep(0); // yield
+ * value_ = temp + incr;
+ * }
+ * int32_t get() { return value_; }
+ * };
+ *
+ * // We use LockWrap on Accumulator to have safe multithread access.
+ * android::mediametrics::LockWrap<Accumulator> a{}; // locked accumulator succeeds
+ *
+ * // Conversely, the following line fails:
+ * // auto a = std::make_shared<Accumulator>(); // this fails, only 50% adds atomic.
+ *
+ * constexpr size_t THREADS = 100;
+ * constexpr size_t ITERATIONS = 10;
+ * constexpr int32_t INCREMENT = 1;
+ *
+ * // Test by generating multiple threads, all adding simultaneously.
+ * std::vector<std::future<void>> threads(THREADS);
+ * for (size_t i = 0; i < THREADS; ++i) {
+ * threads.push_back(std::async(std::launch::async, [&] {
+ * for (size_t j = 0; j < ITERATIONS; ++j) {
+ * a->add(INCREMENT); // add needs locked access here.
+ * }
+ * }));
+ * }
+ * threads.clear();
+ *
+ * // If the add operations are not atomic, value will be smaller than expected.
+ * ASSERT_EQ(INCREMENT * THREADS * ITERATIONS, (size_t)a->get());
+ *
+ */
+template <typename T>
+class LockWrap {
+ /**
+ * Holding class that keeps the pointer and the lock.
+ *
+ * We return this holding class from operator->() to keep the lock until the
+ * method function or method variable access is completed.
+ */
+ class LockedPointer {
+ friend LockWrap;
+ LockedPointer(T *t, std::recursive_mutex *lock, std::atomic<size_t> *recursionDepth)
+ : mT(t), mLock(*lock), mRecursionDepth(recursionDepth) { ++*mRecursionDepth; }
+
+ T* const mT;
+ std::lock_guard<std::recursive_mutex> mLock;
+ std::atomic<size_t>* mRecursionDepth;
+ public:
+ ~LockedPointer() {
+ --*mRecursionDepth; // Used for testing, we do not check underflow.
+ }
+
+ const T* operator->() const {
+ return mT;
+ }
+ T* operator->() {
+ return mT;
+ }
+ };
+
+ // We must use a recursive mutex because the end of the full expression may
+ // involve another reference to T->.
+ //
+ // A recursive mutex allows the same thread to recursively acquire,
+ // but different thread would block.
+ //
+ // Example which fails with a normal mutex:
+ //
+ // android::mediametrics::LockWrap<std::vector<int>> v{std::initializer_list<int>{1, 2}};
+ // const int sum = v->operator[](0) + v->operator[](1);
+ //
+ mutable std::recursive_mutex mLock;
+ mutable T mT;
+ mutable std::atomic<size_t> mRecursionDepth{}; // Used for testing.
+
+public:
+ template <typename... Args>
+ explicit LockWrap(Args&&... args) : mT(std::forward<Args>(args)...) {}
+
+ const LockedPointer operator->() const {
+ return LockedPointer(&mT, &mLock, &mRecursionDepth);
+ }
+ LockedPointer operator->() {
+ return LockedPointer(&mT, &mLock, &mRecursionDepth);
+ }
+
+ // Returns the lock depth of the recursive mutex.
+ // @TestApi
+ size_t getRecursionDepth() const {
+ return mRecursionDepth;
+ }
+};
+
+} // namespace android::mediametrics
diff --git a/services/mediametrics/benchmarks/Android.bp b/services/mediametrics/benchmarks/Android.bp
new file mode 100644
index 0000000..b61f44f
--- /dev/null
+++ b/services/mediametrics/benchmarks/Android.bp
@@ -0,0 +1,6 @@
+cc_test {
+ name: "mediametrics_benchmarks",
+ srcs: ["mediametrics_benchmarks.cpp"],
+ shared_libs: ["libbinder", "libmediametrics",],
+ static_libs: ["libgoogle-benchmark"],
+}
diff --git a/services/mediametrics/benchmarks/README.md b/services/mediametrics/benchmarks/README.md
new file mode 100644
index 0000000..7cf767b
--- /dev/null
+++ b/services/mediametrics/benchmarks/README.md
@@ -0,0 +1,4 @@
+This benchmark may fail occasionally, probably due to the binder queue being full.
+If that happens, just re-run it and it will usually work eventually.
+
+adb shell /data/nativetest64/media\_metrics/media\_metrics
diff --git a/services/mediametrics/benchmarks/mediametrics_benchmarks.cpp b/services/mediametrics/benchmarks/mediametrics_benchmarks.cpp
new file mode 100644
index 0000000..f434867
--- /dev/null
+++ b/services/mediametrics/benchmarks/mediametrics_benchmarks.cpp
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+#include <media/MediaMetricsItem.h>
+#include <benchmark/benchmark.h>
+
+class MyItem : public android::mediametrics::BaseItem {
+public:
+ static bool mySubmitBuffer() {
+ // Deliberately lame so that we're measuring just the cost to deliver things to the service.
+ return submitBuffer("", 0);
+ }
+};
+
+static void BM_SubmitBuffer(benchmark::State& state)
+{
+ while (state.KeepRunning()) {
+ MyItem myItem;
+ bool ok = myItem.mySubmitBuffer();
+ if (ok == false) {
+ // submitBuffer() currently uses one-way binder IPC, which provides unreliable delivery
+ // with at-most-one guarantee.
+ // It is expected that the call may occasionally fail if the one-way queue is full.
+ // The Iterations magic number below was tuned to reduce, but not eliminate, failures.
+ state.SkipWithError("failed");
+ return;
+ }
+ benchmark::ClobberMemory();
+ }
+}
+
+BENCHMARK(BM_SubmitBuffer)->Iterations(4000); // Adjust magic number until test runs
+
+BENCHMARK_MAIN();
diff --git a/services/mediametrics/iface_statsd.cpp b/services/mediametrics/iface_statsd.cpp
index 962cafe..3a1eea7 100644
--- a/services/mediametrics/iface_statsd.cpp
+++ b/services/mediametrics/iface_statsd.cpp
@@ -38,10 +38,10 @@
namespace android {
-// set of routines that crack a MediaAnalyticsItem
+// set of routines that crack a mediametrics::Item
// and send it off to statsd with the appropriate hooks
//
-// each MediaAnalyticsItem type (extractor, codec, nuplayer, etc)
+// each mediametrics::Item type (extractor, codec, nuplayer, etc)
// has its own routine to handle this.
//
@@ -49,7 +49,7 @@
struct statsd_hooks {
const char *key;
- bool (*handler)(const MediaAnalyticsItem *);
+ bool (*handler)(const mediametrics::Item *);
};
// keep this sorted, so we can do binary searches
@@ -70,7 +70,7 @@
};
// give me a record, i'll look at the type and upload appropriately
-bool dump2Statsd(const std::shared_ptr<const MediaAnalyticsItem>& item) {
+bool dump2Statsd(const std::shared_ptr<const mediametrics::Item>& item) {
if (item == NULL) return false;
// get the key
diff --git a/services/mediametrics/iface_statsd.h b/services/mediametrics/iface_statsd.h
index 1c48118..19505a4 100644
--- a/services/mediametrics/iface_statsd.h
+++ b/services/mediametrics/iface_statsd.h
@@ -19,17 +19,17 @@
extern bool enabled_statsd;
// component specific dumpers
-extern bool statsd_audiopolicy(const MediaAnalyticsItem *);
-extern bool statsd_audiorecord(const MediaAnalyticsItem *);
-extern bool statsd_audiothread(const MediaAnalyticsItem *);
-extern bool statsd_audiotrack(const MediaAnalyticsItem *);
-extern bool statsd_codec(const MediaAnalyticsItem *);
-extern bool statsd_extractor(const MediaAnalyticsItem *);
-extern bool statsd_nuplayer(const MediaAnalyticsItem *);
-extern bool statsd_recorder(const MediaAnalyticsItem *);
+extern bool statsd_audiopolicy(const mediametrics::Item *);
+extern bool statsd_audiorecord(const mediametrics::Item *);
+extern bool statsd_audiothread(const mediametrics::Item *);
+extern bool statsd_audiotrack(const mediametrics::Item *);
+extern bool statsd_codec(const mediametrics::Item *);
+extern bool statsd_extractor(const mediametrics::Item *);
+extern bool statsd_nuplayer(const mediametrics::Item *);
+extern bool statsd_recorder(const mediametrics::Item *);
-extern bool statsd_mediadrm(const MediaAnalyticsItem *);
-extern bool statsd_widevineCDM(const MediaAnalyticsItem *);
-extern bool statsd_drmmanager(const MediaAnalyticsItem *);
+extern bool statsd_mediadrm(const mediametrics::Item *);
+extern bool statsd_widevineCDM(const mediametrics::Item *);
+extern bool statsd_drmmanager(const mediametrics::Item *);
} // namespace android
diff --git a/services/mediametrics/main_mediametrics.cpp b/services/mediametrics/main_mediametrics.cpp
index 4b2a9fa..ec392e2 100644
--- a/services/mediametrics/main_mediametrics.cpp
+++ b/services/mediametrics/main_mediametrics.cpp
@@ -34,10 +34,10 @@
// to match the service name
// we're replacing "/system/bin/mediametrics" with "media.metrics"
// we add a ".", but discard the path components: we finish with a shorter string
- strcpy(argv[0], MediaAnalyticsService::kServiceName);
+ strcpy(argv[0], MediaMetricsService::kServiceName);
defaultServiceManager()->addService(
- String16(MediaAnalyticsService::kServiceName), new MediaAnalyticsService());
+ String16(MediaMetricsService::kServiceName), new MediaMetricsService());
sp<ProcessState> processState(ProcessState::self());
// processState->setThreadPoolMaxThreadCount(8);
diff --git a/services/mediametrics/statsd_audiopolicy.cpp b/services/mediametrics/statsd_audiopolicy.cpp
index 2f934f8..634c801 100644
--- a/services/mediametrics/statsd_audiopolicy.cpp
+++ b/services/mediametrics/statsd_audiopolicy.cpp
@@ -37,12 +37,12 @@
namespace android {
-bool statsd_audiopolicy(const MediaAnalyticsItem *item)
+bool statsd_audiopolicy(const mediametrics::Item *item)
{
if (item == NULL) return false;
// these go into the statsd wrapper
- const nsecs_t timestamp = MediaAnalyticsService::roundTime(item->getTimestamp());
+ const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
std::string pkgName = item->getPkgName();
int64_t pkgVersionCode = item->getPkgVersionCode();
int64_t mediaApexVersion = 0;
diff --git a/services/mediametrics/statsd_audiorecord.cpp b/services/mediametrics/statsd_audiorecord.cpp
index 4e2829d..69d1661 100644
--- a/services/mediametrics/statsd_audiorecord.cpp
+++ b/services/mediametrics/statsd_audiorecord.cpp
@@ -37,12 +37,12 @@
namespace android {
-bool statsd_audiorecord(const MediaAnalyticsItem *item)
+bool statsd_audiorecord(const mediametrics::Item *item)
{
if (item == NULL) return false;
// these go into the statsd wrapper
- const nsecs_t timestamp = MediaAnalyticsService::roundTime(item->getTimestamp());
+ const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
std::string pkgName = item->getPkgName();
int64_t pkgVersionCode = item->getPkgVersionCode();
int64_t mediaApexVersion = 0;
diff --git a/services/mediametrics/statsd_audiothread.cpp b/services/mediametrics/statsd_audiothread.cpp
index a5bfcba..300151b 100644
--- a/services/mediametrics/statsd_audiothread.cpp
+++ b/services/mediametrics/statsd_audiothread.cpp
@@ -37,12 +37,12 @@
namespace android {
-bool statsd_audiothread(const MediaAnalyticsItem *item)
+bool statsd_audiothread(const mediametrics::Item *item)
{
if (item == NULL) return false;
// these go into the statsd wrapper
- const nsecs_t timestamp = MediaAnalyticsService::roundTime(item->getTimestamp());
+ const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
std::string pkgName = item->getPkgName();
int64_t pkgVersionCode = item->getPkgVersionCode();
int64_t mediaApexVersion = 0;
diff --git a/services/mediametrics/statsd_audiotrack.cpp b/services/mediametrics/statsd_audiotrack.cpp
index 9d76d43..397cdf3 100644
--- a/services/mediametrics/statsd_audiotrack.cpp
+++ b/services/mediametrics/statsd_audiotrack.cpp
@@ -37,12 +37,12 @@
namespace android {
-bool statsd_audiotrack(const MediaAnalyticsItem *item)
+bool statsd_audiotrack(const mediametrics::Item *item)
{
if (item == NULL) return false;
// these go into the statsd wrapper
- const nsecs_t timestamp = MediaAnalyticsService::roundTime(item->getTimestamp());
+ const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
std::string pkgName = item->getPkgName();
int64_t pkgVersionCode = item->getPkgVersionCode();
int64_t mediaApexVersion = 0;
diff --git a/services/mediametrics/statsd_codec.cpp b/services/mediametrics/statsd_codec.cpp
index fde0bfa..214c51a 100644
--- a/services/mediametrics/statsd_codec.cpp
+++ b/services/mediametrics/statsd_codec.cpp
@@ -37,12 +37,12 @@
namespace android {
-bool statsd_codec(const MediaAnalyticsItem *item)
+bool statsd_codec(const mediametrics::Item *item)
{
if (item == NULL) return false;
// these go into the statsd wrapper
- const nsecs_t timestamp = MediaAnalyticsService::roundTime(item->getTimestamp());
+ const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
std::string pkgName = item->getPkgName();
int64_t pkgVersionCode = item->getPkgVersionCode();
int64_t mediaApexVersion = 0;
diff --git a/services/mediametrics/statsd_drm.cpp b/services/mediametrics/statsd_drm.cpp
index 78d0a22..b12f4f3 100644
--- a/services/mediametrics/statsd_drm.cpp
+++ b/services/mediametrics/statsd_drm.cpp
@@ -38,11 +38,11 @@
namespace android {
// mediadrm
-bool statsd_mediadrm(const MediaAnalyticsItem *item)
+bool statsd_mediadrm(const mediametrics::Item *item)
{
if (item == NULL) return false;
- const nsecs_t timestamp = MediaAnalyticsService::roundTime(item->getTimestamp());
+ const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
std::string pkgName = item->getPkgName();
int64_t pkgVersionCode = item->getPkgVersionCode();
int64_t mediaApexVersion = 0;
@@ -75,11 +75,11 @@
}
// widevineCDM
-bool statsd_widevineCDM(const MediaAnalyticsItem *item)
+bool statsd_widevineCDM(const mediametrics::Item *item)
{
if (item == NULL) return false;
- const nsecs_t timestamp = MediaAnalyticsService::roundTime(item->getTimestamp());
+ const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
std::string pkgName = item->getPkgName();
int64_t pkgVersionCode = item->getPkgVersionCode();
int64_t mediaApexVersion = 0;
@@ -105,11 +105,11 @@
}
// drmmanager
-bool statsd_drmmanager(const MediaAnalyticsItem *item)
+bool statsd_drmmanager(const mediametrics::Item *item)
{
if (item == NULL) return false;
- const nsecs_t timestamp = MediaAnalyticsService::roundTime(item->getTimestamp());
+ const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
std::string pkgName = item->getPkgName();
int64_t pkgVersionCode = item->getPkgVersionCode();
int64_t mediaApexVersion = 0;
diff --git a/services/mediametrics/statsd_extractor.cpp b/services/mediametrics/statsd_extractor.cpp
index cc62241..8574358 100644
--- a/services/mediametrics/statsd_extractor.cpp
+++ b/services/mediametrics/statsd_extractor.cpp
@@ -37,12 +37,12 @@
namespace android {
-bool statsd_extractor(const MediaAnalyticsItem *item)
+bool statsd_extractor(const mediametrics::Item *item)
{
if (item == NULL) return false;
// these go into the statsd wrapper
- const nsecs_t timestamp = MediaAnalyticsService::roundTime(item->getTimestamp());
+ const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
std::string pkgName = item->getPkgName();
int64_t pkgVersionCode = item->getPkgVersionCode();
int64_t mediaApexVersion = 0;
diff --git a/services/mediametrics/statsd_nuplayer.cpp b/services/mediametrics/statsd_nuplayer.cpp
index 9db1e81..df7e59f 100644
--- a/services/mediametrics/statsd_nuplayer.cpp
+++ b/services/mediametrics/statsd_nuplayer.cpp
@@ -41,12 +41,12 @@
* handles nuplayer AND nuplayer2
* checks for the union of what the two players generate
*/
-bool statsd_nuplayer(const MediaAnalyticsItem *item)
+bool statsd_nuplayer(const mediametrics::Item *item)
{
if (item == NULL) return false;
// these go into the statsd wrapper
- const nsecs_t timestamp = MediaAnalyticsService::roundTime(item->getTimestamp());
+ const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
std::string pkgName = item->getPkgName();
int64_t pkgVersionCode = item->getPkgVersionCode();
int64_t mediaApexVersion = 0;
diff --git a/services/mediametrics/statsd_recorder.cpp b/services/mediametrics/statsd_recorder.cpp
index 972a221..4de1746 100644
--- a/services/mediametrics/statsd_recorder.cpp
+++ b/services/mediametrics/statsd_recorder.cpp
@@ -37,12 +37,12 @@
namespace android {
-bool statsd_recorder(const MediaAnalyticsItem *item)
+bool statsd_recorder(const mediametrics::Item *item)
{
if (item == NULL) return false;
// these go into the statsd wrapper
- const nsecs_t timestamp = MediaAnalyticsService::roundTime(item->getTimestamp());
+ const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
std::string pkgName = item->getPkgName();
int64_t pkgVersionCode = item->getPkgVersionCode();
int64_t mediaApexVersion = 0;
diff --git a/services/mediametrics/tests/Android.bp b/services/mediametrics/tests/Android.bp
index 9eb2d89..bdeda30 100644
--- a/services/mediametrics/tests/Android.bp
+++ b/services/mediametrics/tests/Android.bp
@@ -17,6 +17,7 @@
"liblog",
"libmediametrics",
"libmediametricsservice",
+ "libmediautils",
"libutils",
],
diff --git a/services/mediametrics/tests/build_and_run_all_unit_tests.sh b/services/mediametrics/tests/build_and_run_all_unit_tests.sh
index 2511c30..382be47 100755
--- a/services/mediametrics/tests/build_and_run_all_unit_tests.sh
+++ b/services/mediametrics/tests/build_and_run_all_unit_tests.sh
@@ -20,5 +20,5 @@
echo "========================================"
echo "testing mediametrics"
-adb push $OUT/data/nativetest/mediametrics_tests/mediametrics_tests /system/bin
+adb push $OUT/data/nativetest64/mediametrics_tests/mediametrics_tests /system/bin
adb shell /system/bin/mediametrics_tests
diff --git a/services/mediametrics/tests/mediametrics_tests.cpp b/services/mediametrics/tests/mediametrics_tests.cpp
index 808da7b..27b72eb 100644
--- a/services/mediametrics/tests/mediametrics_tests.cpp
+++ b/services/mediametrics/tests/mediametrics_tests.cpp
@@ -22,7 +22,7 @@
#include <stdio.h>
#include <gtest/gtest.h>
-#include <media/MediaAnalyticsItem.h>
+#include <media/MediaMetricsItem.h>
using namespace android;
@@ -35,12 +35,163 @@
return count;
}
+TEST(mediametrics_tests, startsWith) {
+ std::string s("test");
+ ASSERT_EQ(true, android::mediametrics::startsWith(s, "te"));
+ ASSERT_EQ(true, android::mediametrics::startsWith(s, std::string("tes")));
+ ASSERT_EQ(false, android::mediametrics::startsWith(s, "ts"));
+ ASSERT_EQ(false, android::mediametrics::startsWith(s, std::string("est")));
+}
+
+TEST(mediametrics_tests, defer) {
+ bool check = false;
+ {
+ android::mediametrics::Defer defer([&] { check = true; });
+ ASSERT_EQ(false, check);
+ }
+ ASSERT_EQ(true, check);
+}
+
+TEST(mediametrics_tests, shared_ptr_wrap) {
+ // Test shared pointer wrap with simple access
+ android::mediametrics::SharedPtrWrap<std::string> s("123");
+ ASSERT_EQ('1', s->at(0));
+ ASSERT_EQ('2', s->at(1));
+ s->push_back('4');
+ ASSERT_EQ('4', s->at(3));
+
+ const android::mediametrics::SharedPtrWrap<std::string> s2("345");
+ ASSERT_EQ('3', s2->operator[](0)); // s2[0] == '3'
+ // we allow modification through a const shared pointer wrap
+ // for compatibility with shared_ptr.
+ s2->push_back('6');
+ ASSERT_EQ('6', s2->operator[](3)); // s2[3] == '6'
+
+ android::mediametrics::SharedPtrWrap<std::string> s3("");
+ s3.set(std::make_shared<std::string>("abc"));
+ ASSERT_EQ('b', s3->operator[](1)); // s2[1] = 'b';
+
+ // Use Thunk to check whether the destructor was called prematurely
+ // when setting the shared ptr wrap in the middle of a method.
+
+ class Thunk {
+ std::function<void(int)> mF;
+ const int mFinal;
+
+ public:
+ Thunk(decltype(mF) f, int final) : mF(f), mFinal(final) {}
+ ~Thunk() { mF(mFinal); }
+ void thunk(int value) { mF(value); }
+ };
+
+ int counter = 0;
+ android::mediametrics::SharedPtrWrap<Thunk> s4(
+ [&](int value) {
+ s4.set(std::make_shared<Thunk>([](int){}, 0)); // recursively set s4 while in s4.
+ ++counter;
+ ASSERT_EQ(value, counter); // on thunk() value is 1, on destructor this is 2.
+ }, 2);
+
+ // This will fail if the shared ptr wrap doesn't hold a ref count during method access.
+ s4->thunk(1);
+}
+
+TEST(mediametrics_tests, lock_wrap) {
+ // Test lock wrap with simple access
+ android::mediametrics::LockWrap<std::string> s("123");
+ ASSERT_EQ('1', s->at(0));
+ ASSERT_EQ('2', s->at(1));
+ s->push_back('4');
+ ASSERT_EQ('4', s->at(3));
+
+ const android::mediametrics::LockWrap<std::string> s2("345");
+ ASSERT_EQ('3', s2->operator[](0)); // s2[0] == '3'
+ // note: we can't modify s2 due to const, s2->push_back('6');
+
+ android::mediametrics::LockWrap<std::string> s3("");
+ s3->operator=("abc");
+ ASSERT_EQ('b', s3->operator[](1)); // s2[1] = 'b';
+
+ // Check that we can recursively hold lock.
+ android::mediametrics::LockWrap<std::vector<int>> v{std::initializer_list<int>{1, 2}};
+ v->push_back(3);
+ v->push_back(4);
+ ASSERT_EQ(1, v->operator[](0));
+ ASSERT_EQ(2, v->operator[](1));
+ ASSERT_EQ(3, v->operator[](2));
+ ASSERT_EQ(4, v->operator[](3));
+ // The end of the full expression here requires recursive depth of 4.
+ ASSERT_EQ(10, v->operator[](0) + v->operator[](1) + v->operator[](2) + v->operator[](3));
+
+ // Mikhail's note: a non-recursive lock implementation could be used if one obtains
+ // the LockedPointer helper object like this and directly hold the lock through RAII,
+ // though it is trickier in use.
+ //
+ // We include an example here for completeness.
+ {
+ auto l = v.operator->();
+ ASSERT_EQ(10, l->operator[](0) + l->operator[](1) + l->operator[](2) + l->operator[](3));
+ }
+
+ // Use Thunk to check whether we have the lock when calling a method through LockWrap.
+
+ class Thunk {
+ std::function<void()> mF;
+
+ public:
+ Thunk(decltype(mF) f) : mF(f) {}
+ void thunk() { mF(); }
+ };
+
+ android::mediametrics::LockWrap<Thunk> s4([&]{
+ ASSERT_EQ((size_t)1, s4.getRecursionDepth()); // we must be locked when thunk() is called.
+ });
+
+ ASSERT_EQ((size_t)0, s4.getRecursionDepth());
+ // This will fail if we are not locked during method access.
+ s4->thunk();
+ ASSERT_EQ((size_t)0, s4.getRecursionDepth());
+}
+
+TEST(mediametrics_tests, lock_wrap_multithread) {
+ class Accumulator {
+ int32_t value_ = 0;
+ public:
+ void add(int32_t incr) {
+ const int32_t temp = value_;
+ sleep(0); // yield
+ value_ = temp + incr;
+ }
+ int32_t get() { return value_; }
+ };
+
+ android::mediametrics::LockWrap<Accumulator> a{}; // locked accumulator succeeds
+ // auto a = std::make_shared<Accumulator>(); // this fails, only 50% adds atomic.
+
+ constexpr size_t THREADS = 100;
+ constexpr size_t ITERATIONS = 10;
+ constexpr int32_t INCREMENT = 1;
+
+ std::vector<std::future<void>> threads(THREADS);
+ for (size_t i = 0; i < THREADS; ++i) {
+ threads.push_back(std::async(std::launch::async, [&] {
+ for (size_t j = 0; j < ITERATIONS; ++j) {
+ a->add(INCREMENT);
+ }
+ }));
+ }
+ threads.clear();
+
+ // If the add operations are not atomic, value will be smaller than expected.
+ ASSERT_EQ(INCREMENT * THREADS * ITERATIONS, (size_t)a->get());
+}
+
TEST(mediametrics_tests, instantiate) {
- sp mediaMetrics = new MediaAnalyticsService();
+ sp mediaMetrics = new MediaMetricsService();
status_t status;
// random keys ignored when empty
- std::unique_ptr<MediaAnalyticsItem> random_key(MediaAnalyticsItem::create("random_key"));
+ std::unique_ptr<mediametrics::Item> random_key(mediametrics::Item::create("random_key"));
status = mediaMetrics->submit(random_key.get());
ASSERT_EQ(PERMISSION_DENIED, status);
@@ -50,7 +201,7 @@
ASSERT_EQ(PERMISSION_DENIED, status);
// known keys ignored if empty
- std::unique_ptr<MediaAnalyticsItem> audiotrack_key(MediaAnalyticsItem::create("audiotrack"));
+ std::unique_ptr<mediametrics::Item> audiotrack_key(mediametrics::Item::create("audiotrack"));
status = mediaMetrics->submit(audiotrack_key.get());
ASSERT_EQ(BAD_VALUE, status);
@@ -62,7 +213,7 @@
/*
// fluent style that goes directly to mediametrics
- ASSERT_EQ(true, MediaAnalyticsItem("audiorecord")
+ ASSERT_EQ(true, mediametrics::Item("audiorecord")
.setInt32("value", 2)
.addInt32("bar", 1)
.addInt32("value", 3)
@@ -72,8 +223,34 @@
mediaMetrics->dump(fileno(stdout), {} /* args */);
}
+TEST(mediametrics_tests, package_installer_check) {
+ ASSERT_EQ(false, MediaMetricsService::useUidForPackage(
+ "abcd", "installer")); // ok, package name has no dot.
+ ASSERT_EQ(false, MediaMetricsService::useUidForPackage(
+ "android.com", "installer")); // ok, package name starts with android
+
+ ASSERT_EQ(false, MediaMetricsService::useUidForPackage(
+ "abc.def", "com.android.foo")); // ok, installer name starts with com.android
+ ASSERT_EQ(false, MediaMetricsService::useUidForPackage(
+ "123.456", "com.google.bar")); // ok, installer name starts with com.google
+ ASSERT_EQ(false, MediaMetricsService::useUidForPackage(
+ "r2.d2", "preload")); // ok, installer name is preload
+
+ ASSERT_EQ(true, MediaMetricsService::useUidForPackage(
+ "abc.def", "installer")); // unknown installer
+ ASSERT_EQ(true, MediaMetricsService::useUidForPackage(
+ "123.456", "installer")); // unknown installer
+ ASSERT_EQ(true, MediaMetricsService::useUidForPackage(
+ "r2.d2", "preload23")); // unknown installer
+
+ ASSERT_EQ(true, MediaMetricsService::useUidForPackage(
+ "com.android.foo", "abc.def")); // unknown installer
+ ASSERT_EQ(true, MediaMetricsService::useUidForPackage(
+ "com.google.bar", "123.456")); // unknown installer
+}
+
TEST(mediametrics_tests, item_manipulation) {
- MediaAnalyticsItem item("audiorecord");
+ mediametrics::Item item("audiorecord");
item.setInt32("value", 2).addInt32("bar", 3).addInt32("value", 4);
@@ -128,14 +305,14 @@
printf("item: %s\n", item.toString().c_str());
fflush(stdout);
- sp mediaMetrics = new MediaAnalyticsService();
+ sp mediaMetrics = new MediaMetricsService();
status_t status = mediaMetrics->submit(&item);
ASSERT_EQ(NO_ERROR, status);
mediaMetrics->dump(fileno(stdout), {} /* args */);
}
TEST(mediametrics_tests, superbig_item) {
- MediaAnalyticsItem item("TheBigOne");
+ mediametrics::Item item("TheBigOne");
constexpr size_t count = 10000;
for (size_t i = 0; i < count; ++i) {
@@ -149,7 +326,7 @@
}
TEST(mediametrics_tests, superbig_item_removal) {
- MediaAnalyticsItem item("TheOddBigOne");
+ mediametrics::Item item("TheOddBigOne");
constexpr size_t count = 10000;
for (size_t i = 0; i < count; ++i) {
@@ -170,7 +347,7 @@
}
TEST(mediametrics_tests, superbig_item_removal2) {
- MediaAnalyticsItem item("TheOne");
+ mediametrics::Item item("TheOne");
constexpr size_t count = 10000;
for (size_t i = 0; i < count; ++i) {
@@ -191,7 +368,7 @@
}
TEST(mediametrics_tests, item_transmutation) {
- MediaAnalyticsItem item("Alchemist's Stone");
+ mediametrics::Item item("Alchemist's Stone");
item.setInt64("convert", 123);
int64_t i64;
@@ -207,7 +384,7 @@
}
TEST(mediametrics_tests, item_binderization) {
- MediaAnalyticsItem item;
+ mediametrics::Item item;
item.setInt32("i32", 1)
.setInt64("i64", 2)
.setDouble("double", 3.1)
@@ -218,14 +395,14 @@
item.writeToParcel(&p);
p.setDataPosition(0); // rewind for reading
- MediaAnalyticsItem item2;
+ mediametrics::Item item2;
item2.readFromParcel(p);
ASSERT_EQ(item, item2);
}
TEST(mediametrics_tests, item_byteserialization) {
- MediaAnalyticsItem item;
+ mediametrics::Item item;
item.setInt32("i32", 1)
.setInt64("i64", 2)
.setDouble("double", 3.1)
@@ -237,7 +414,7 @@
ASSERT_EQ(0, item.writeToByteString(&data, &length));
ASSERT_GT(length, (size_t)0);
- MediaAnalyticsItem item2;
+ mediametrics::Item item2;
item2.readFromByteString(data, length);
printf("item: %s\n", item.toString().c_str());
@@ -248,7 +425,7 @@
}
TEST(mediametrics_tests, item_iteration) {
- MediaAnalyticsItem item;
+ mediametrics::Item item;
item.setInt32("i32", 1)
.setInt64("i64", 2)
.setDouble("double", 3.125)
@@ -262,27 +439,32 @@
int32_t i32;
ASSERT_TRUE(prop.get(&i32));
ASSERT_EQ(1, i32);
+ ASSERT_EQ(1, std::get<int32_t>(prop.get()));
mask |= 1;
} else if (!strcmp(name, "i64")) {
int64_t i64;
ASSERT_TRUE(prop.get(&i64));
ASSERT_EQ(2, i64);
+ ASSERT_EQ(2, std::get<int64_t>(prop.get()));
mask |= 2;
} else if (!strcmp(name, "double")) {
double d;
ASSERT_TRUE(prop.get(&d));
ASSERT_EQ(3.125, d);
+ ASSERT_EQ(3.125, std::get<double>(prop.get()));
mask |= 4;
} else if (!strcmp(name, "string")) {
- const char *s;
+ std::string s;
ASSERT_TRUE(prop.get(&s));
- ASSERT_EQ(0, strcmp(s, "abc"));
+ ASSERT_EQ("abc", s);
+ ASSERT_EQ(s, std::get<std::string>(prop.get()));
mask |= 8;
} else if (!strcmp(name, "rate")) {
std::pair<int64_t, int64_t> r;
ASSERT_TRUE(prop.get(&r));
ASSERT_EQ(11, r.first);
ASSERT_EQ(12, r.second);
+ ASSERT_EQ(r, std::get<decltype(r)>(prop.get()));
mask |= 16;
} else {
FAIL();
@@ -292,7 +474,7 @@
}
TEST(mediametrics_tests, item_expansion) {
- mediametrics::Item<1> item("I");
+ mediametrics::LogItem<1> item("I");
item.set("i32", (int32_t)1)
.set("i64", (int64_t)2)
.set("double", (double)3.125)
@@ -300,7 +482,7 @@
.set("rate", std::pair<int64_t, int64_t>(11, 12));
ASSERT_TRUE(item.updateHeader());
- MediaAnalyticsItem item2;
+ mediametrics::Item item2;
item2.readFromByteString(item.getBuffer(), item.getLength());
ASSERT_EQ((pid_t)-1, item2.getPid());
ASSERT_EQ((uid_t)-1, item2.getUid());
@@ -323,9 +505,9 @@
ASSERT_EQ(3.125, d);
mask |= 4;
} else if (!strcmp(name, "string")) {
- const char *s;
+ std::string s;
ASSERT_TRUE(prop.get(&s));
- ASSERT_EQ(0, strcmp(s, "abcdefghijklmnopqrstuvwxyz"));
+ ASSERT_EQ("abcdefghijklmnopqrstuvwxyz", s);
mask |= 8;
} else if (!strcmp(name, "rate")) {
std::pair<int64_t, int64_t> r;
@@ -341,7 +523,7 @@
}
TEST(mediametrics_tests, item_expansion2) {
- mediametrics::Item<1> item("Bigly");
+ mediametrics::LogItem<1> item("Bigly");
item.setPid(123)
.setUid(456);
constexpr size_t count = 10000;
@@ -352,7 +534,7 @@
}
ASSERT_TRUE(item.updateHeader());
- MediaAnalyticsItem item2;
+ mediametrics::Item item2;
printf("begin buffer:%p length:%zu\n", item.getBuffer(), item.getLength());
fflush(stdout);
item2.readFromByteString(item.getBuffer(), item.getLength());
@@ -367,7 +549,7 @@
}
TEST(mediametrics_tests, time_machine_storage) {
- auto item = std::make_shared<MediaAnalyticsItem>("Key");
+ auto item = std::make_shared<mediametrics::Item>("Key");
(*item).set("i32", (int32_t)1)
.set("i64", (int64_t)2)
.set("double", (double)3.125)
@@ -414,21 +596,21 @@
}
TEST(mediametrics_tests, time_machine_remote_key) {
- auto item = std::make_shared<MediaAnalyticsItem>("Key1");
+ auto item = std::make_shared<mediametrics::Item>("Key1");
(*item).set("one", (int32_t)1)
.set("two", (int32_t)2);
android::mediametrics::TimeMachine timeMachine;
ASSERT_EQ(NO_ERROR, timeMachine.put(item, true));
- auto item2 = std::make_shared<MediaAnalyticsItem>("Key2");
+ auto item2 = std::make_shared<mediametrics::Item>("Key2");
(*item2).set("three", (int32_t)3)
.set("[Key1]four", (int32_t)4) // affects Key1
.set("[Key1]five", (int32_t)5); // affects key1
ASSERT_EQ(NO_ERROR, timeMachine.put(item2, true));
- auto item3 = std::make_shared<MediaAnalyticsItem>("Key2");
+ auto item3 = std::make_shared<mediametrics::Item>("Key2");
(*item3).set("six", (int32_t)6)
.set("[Key1]seven", (int32_t)7); // affects Key1
@@ -464,7 +646,7 @@
}
TEST(mediametrics_tests, time_machine_gc) {
- auto item = std::make_shared<MediaAnalyticsItem>("Key1");
+ auto item = std::make_shared<mediametrics::Item>("Key1");
(*item).set("one", (int32_t)1)
.set("two", (int32_t)2)
.setTimestamp(10);
@@ -477,7 +659,7 @@
ASSERT_EQ((size_t)1, timeMachine.size());
- auto item2 = std::make_shared<MediaAnalyticsItem>("Key2");
+ auto item2 = std::make_shared<mediametrics::Item>("Key2");
(*item2).set("three", (int32_t)3)
.set("[Key1]three", (int32_t)3)
.setTimestamp(11);
@@ -487,7 +669,7 @@
//printf("Before\n%s\n\n", timeMachine.dump().c_str());
- auto item3 = std::make_shared<MediaAnalyticsItem>("Key3");
+ auto item3 = std::make_shared<mediametrics::Item>("Key3");
(*item3).set("six", (int32_t)6)
.set("[Key1]four", (int32_t)4) // affects Key1
.set("[Key1]five", (int32_t)5) // affects key1
@@ -515,7 +697,7 @@
}
TEST(mediametrics_tests, transaction_log_gc) {
- auto item = std::make_shared<MediaAnalyticsItem>("Key1");
+ auto item = std::make_shared<mediametrics::Item>("Key1");
(*item).set("one", (int32_t)1)
.set("two", (int32_t)2)
.setTimestamp(10);
@@ -526,7 +708,7 @@
ASSERT_EQ(NO_ERROR, transactionLog.put(item));
ASSERT_EQ((size_t)1, transactionLog.size());
- auto item2 = std::make_shared<MediaAnalyticsItem>("Key2");
+ auto item2 = std::make_shared<mediametrics::Item>("Key2");
(*item2).set("three", (int32_t)3)
.set("[Key1]three", (int32_t)3)
.setTimestamp(11);
@@ -534,7 +716,7 @@
ASSERT_EQ(NO_ERROR, transactionLog.put(item2));
ASSERT_EQ((size_t)2, transactionLog.size());
- auto item3 = std::make_shared<MediaAnalyticsItem>("Key3");
+ auto item3 = std::make_shared<mediametrics::Item>("Key3");
(*item3).set("six", (int32_t)6)
.set("[Key1]four", (int32_t)4) // affects Key1
.set("[Key1]five", (int32_t)5) // affects key1
@@ -544,17 +726,72 @@
ASSERT_EQ((size_t)2, transactionLog.size());
}
+TEST(mediametrics_tests, analytics_actions) {
+ mediametrics::AnalyticsActions analyticsActions;
+ bool action1 = false;
+ bool action2 = false;
+ bool action3 = false;
+ bool action4 = false;
+
+ // check to see whether various actions have been matched.
+ analyticsActions.addAction(
+ "audio.flinger.event",
+ std::string("AudioFlinger"),
+ std::make_shared<mediametrics::AnalyticsActions::Function>(
+ [&](const std::shared_ptr<const android::mediametrics::Item> &) {
+ action1 = true;
+ }));
+
+ analyticsActions.addAction(
+ "audio.*.event",
+ std::string("AudioFlinger"),
+ std::make_shared<mediametrics::AnalyticsActions::Function>(
+ [&](const std::shared_ptr<const android::mediametrics::Item> &) {
+ action2 = true;
+ }));
+
+ analyticsActions.addAction("audio.fl*n*g*r.event",
+ std::string("AudioFlinger"),
+ std::make_shared<mediametrics::AnalyticsActions::Function>(
+ [&](const std::shared_ptr<const android::mediametrics::Item> &) {
+ action3 = true;
+ }));
+
+ analyticsActions.addAction("audio.fl*gn*r.event",
+ std::string("AudioFlinger"),
+ std::make_shared<mediametrics::AnalyticsActions::Function>(
+ [&](const std::shared_ptr<const android::mediametrics::Item> &) {
+ action4 = true;
+ }));
+
+ // make a test item
+ auto item = std::make_shared<mediametrics::Item>("audio.flinger");
+ (*item).set("event", "AudioFlinger");
+
+ // get the actions and execute them
+ auto actions = analyticsActions.getActionsForItem(item);
+ for (const auto& action : actions) {
+ action->operator()(item);
+ }
+
+ // The following should match.
+ ASSERT_EQ(true, action1);
+ ASSERT_EQ(true, action2);
+ ASSERT_EQ(true, action3);
+ ASSERT_EQ(false, action4); // audio.fl*gn*r != audio.flinger
+}
+
TEST(mediametrics_tests, audio_analytics_permission) {
- auto item = std::make_shared<MediaAnalyticsItem>("audio.1");
+ auto item = std::make_shared<mediametrics::Item>("audio.1");
(*item).set("one", (int32_t)1)
.set("two", (int32_t)2)
.setTimestamp(10);
- auto item2 = std::make_shared<MediaAnalyticsItem>("audio.1");
+ auto item2 = std::make_shared<mediametrics::Item>("audio.1");
(*item2).set("three", (int32_t)3)
.setTimestamp(11);
- auto item3 = std::make_shared<MediaAnalyticsItem>("audio.2");
+ auto item3 = std::make_shared<mediametrics::Item>("audio.2");
(*item3).set("four", (int32_t)4)
.setTimestamp(12);
@@ -566,27 +803,27 @@
// TODO: Verify contents of AudioAnalytics.
// Currently there is no getter API in AudioAnalytics besides dump.
- ASSERT_EQ(4, audioAnalytics.dump(1000).second /* lines */);
+ ASSERT_EQ(9, audioAnalytics.dump(1000).second /* lines */);
ASSERT_EQ(NO_ERROR, audioAnalytics.submit(item, true /* isTrusted */));
// untrusted entities can add to an existing key
ASSERT_EQ(NO_ERROR, audioAnalytics.submit(item2, false /* isTrusted */));
// Check that we have some info in the dump.
- ASSERT_LT(4, audioAnalytics.dump(1000).second /* lines */);
+ ASSERT_LT(9, audioAnalytics.dump(1000).second /* lines */);
}
TEST(mediametrics_tests, audio_analytics_dump) {
- auto item = std::make_shared<MediaAnalyticsItem>("audio.1");
+ auto item = std::make_shared<mediametrics::Item>("audio.1");
(*item).set("one", (int32_t)1)
.set("two", (int32_t)2)
.setTimestamp(10);
- auto item2 = std::make_shared<MediaAnalyticsItem>("audio.1");
+ auto item2 = std::make_shared<mediametrics::Item>("audio.1");
(*item2).set("three", (int32_t)3)
.setTimestamp(11);
- auto item3 = std::make_shared<MediaAnalyticsItem>("audio.2");
+ auto item3 = std::make_shared<mediametrics::Item>("audio.2");
(*item3).set("four", (int32_t)4)
.setTimestamp(12);
diff --git a/services/mediatranscoding/Android.bp b/services/mediatranscoding/Android.bp
index 3dc43f1..17347a9 100644
--- a/services/mediatranscoding/Android.bp
+++ b/services/mediatranscoding/Android.bp
@@ -5,8 +5,11 @@
srcs: ["MediaTranscodingService.cpp"],
shared_libs: [
+ "libbase",
"libbinder_ndk",
"liblog",
+ "libmediatranscoding",
+ "libutils",
],
static_libs: [
@@ -27,11 +30,13 @@
],
shared_libs: [
+ "libbase",
// TODO(hkuang): Use libbinder_ndk
"libbinder",
"libutils",
"liblog",
"libbase",
+ "libmediatranscoding",
"libmediatranscodingservice",
],
diff --git a/services/mediatranscoding/MediaTranscodingService.cpp b/services/mediatranscoding/MediaTranscodingService.cpp
index 2680cee..82d4161 100644
--- a/services/mediatranscoding/MediaTranscodingService.cpp
+++ b/services/mediatranscoding/MediaTranscodingService.cpp
@@ -16,20 +16,37 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaTranscodingService"
-#include "MediaTranscodingService.h"
-
+#include <MediaTranscodingService.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
+#include <private/android_filesystem_config.h>
#include <utils/Log.h>
#include <utils/Vector.h>
namespace android {
-namespace media {
+// Convenience methods for constructing binder::Status objects for error returns
+#define STATUS_ERROR_FMT(errorCode, errorString, ...) \
+ Status::fromServiceSpecificErrorWithMessage( \
+ errorCode, \
+ String8::format("%s:%d: " errorString, __FUNCTION__, __LINE__, ##__VA_ARGS__))
-using android::media::MediaTranscodingService;
+// Can MediaTranscoding service trust the caller based on the calling UID?
+// TODO(hkuang): Add MediaProvider's UID.
+static bool isTrustedCallingUid(uid_t uid) {
+ switch (uid) {
+ case AID_ROOT: // root user
+ case AID_SYSTEM:
+ case AID_SHELL:
+ case AID_MEDIA: // mediaserver
+ return true;
+ default:
+ return false;
+ }
+}
-MediaTranscodingService::MediaTranscodingService() {
+MediaTranscodingService::MediaTranscodingService()
+ : mTranscodingClientManager(TranscodingClientManager::getInstance()) {
ALOGV("MediaTranscodingService is created");
}
@@ -37,9 +54,17 @@
ALOGE("Should not be in ~MediaTranscodingService");
}
-binder_status_t MediaTranscodingService::dump(int /* fd */, const char** /*args*/,
- uint32_t /*numArgs*/) {
- // TODO(hkuang): Add implementation.
+binder_status_t MediaTranscodingService::dump(int fd, const char** /*args*/, uint32_t /*numArgs*/) {
+ String8 result;
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+
+ snprintf(buffer, SIZE, "MediaTranscodingService: %p\n", this);
+ result.append(buffer);
+ write(fd, result.string(), result.size());
+
+ Vector<String16> args;
+ mTranscodingClientManager.dumpAllClients(fd, args);
return OK;
}
@@ -55,20 +80,104 @@
}
Status MediaTranscodingService::registerClient(
- const std::shared_ptr<ITranscodingServiceClient>& /*in_client*/,
- int32_t* /*_aidl_return*/) {
- // TODO(hkuang): Add implementation.
+ const std::shared_ptr<ITranscodingServiceClient>& in_client,
+ const std::string& in_opPackageName, int32_t in_clientUid, int32_t in_clientPid,
+ int32_t* _aidl_return) {
+ if (in_client == nullptr) {
+ ALOGE("Client can not be null");
+ *_aidl_return = kInvalidJobId;
+ return Status::fromServiceSpecificError(ERROR_ILLEGAL_ARGUMENT);
+ }
+
+ int32_t callingPid = AIBinder_getCallingPid();
+ int32_t callingUid = AIBinder_getCallingUid();
+
+ // Check if we can trust clientUid. Only privilege caller could forward the uid on app client's behalf.
+ if (in_clientUid == USE_CALLING_UID) {
+ in_clientUid = callingUid;
+ } else if (!isTrustedCallingUid(callingUid)) {
+ ALOGE("MediaTranscodingService::registerClient failed (calling PID %d, calling UID %d) "
+ "rejected "
+ "(don't trust clientUid %d)",
+ in_clientPid, in_clientUid, in_clientUid);
+ return STATUS_ERROR_FMT(ERROR_PERMISSION_DENIED,
+ "Untrusted caller (calling PID %d, UID %d) trying to "
+ "register client",
+ in_clientPid, in_clientUid);
+ }
+
+ // Check if we can trust clientPid. Only privilege caller could forward the pid on app client's behalf.
+ if (in_clientPid == USE_CALLING_PID) {
+ in_clientPid = callingPid;
+ } else if (!isTrustedCallingUid(callingUid)) {
+ ALOGE("MediaTranscodingService::registerClient client failed (calling PID %d, calling UID "
+ "%d) rejected "
+ "(don't trust clientPid %d)",
+ in_clientPid, in_clientUid, in_clientPid);
+ return STATUS_ERROR_FMT(ERROR_PERMISSION_DENIED,
+ "Untrusted caller (calling PID %d, UID %d) trying to "
+ "register client",
+ in_clientPid, in_clientUid);
+ }
+
+ // We know the clientId must be equal to its pid as we assigned client's pid as its clientId.
+ int32_t clientId = in_clientPid;
+
+ // Checks if the client already registers.
+ if (mTranscodingClientManager.isClientIdRegistered(clientId)) {
+ return Status::fromServiceSpecificError(ERROR_ALREADY_EXISTS);
+ }
+
+ // Creates the client and uses its process id as client id.
+ std::unique_ptr<TranscodingClientManager::ClientInfo> newClient =
+ std::make_unique<TranscodingClientManager::ClientInfo>(
+ in_client, clientId, in_clientPid, in_clientUid, in_opPackageName);
+ status_t err = mTranscodingClientManager.addClient(std::move(newClient));
+ if (err != OK) {
+ *_aidl_return = kInvalidClientId;
+ return STATUS_ERROR_FMT(err, "Failed to add client to TranscodingClientManager");
+ }
+
+ ALOGD("Assign client: %s pid: %d, uid: %d with id: %d", in_opPackageName.c_str(), in_clientPid,
+ in_clientUid, clientId);
+
+ *_aidl_return = clientId;
return Status::ok();
}
-Status MediaTranscodingService::unregisterClient(int32_t /*clientId*/, bool* /*_aidl_return*/) {
- // TODO(hkuang): Add implementation.
+Status MediaTranscodingService::unregisterClient(int32_t clientId, bool* _aidl_return) {
+ ALOGD("unregisterClient id: %d", clientId);
+ int32_t callingUid = AIBinder_getCallingUid();
+ int32_t callingPid = AIBinder_getCallingPid();
+
+ // Only the client with clientId or the trusted caller could unregister the client.
+ if (callingPid != clientId) {
+ if (!isTrustedCallingUid(callingUid)) {
+ ALOGE("Untrusted caller (calling PID %d, UID %d) trying to "
+ "unregister client with id: %d",
+ callingUid, callingPid, clientId);
+ *_aidl_return = true;
+ return STATUS_ERROR_FMT(ERROR_PERMISSION_DENIED,
+ "Untrusted caller (calling PID %d, UID %d) trying to "
+ "unregister client with id: %d",
+ callingUid, callingPid, clientId);
+ }
+ }
+
+ *_aidl_return = (mTranscodingClientManager.removeClient(clientId) == OK);
+ return Status::ok();
+}
+
+Status MediaTranscodingService::getNumOfClients(int32_t* _aidl_return) {
+ ALOGD("MediaTranscodingService::getNumOfClients");
+ *_aidl_return = mTranscodingClientManager.getNumOfClients();
return Status::ok();
}
Status MediaTranscodingService::submitRequest(int32_t /*clientId*/,
- const TranscodingRequest& /*request*/,
- TranscodingJob* /*job*/, int32_t* /*_aidl_return*/) {
+ const TranscodingRequestParcel& /*request*/,
+ TranscodingJobParcel* /*job*/,
+ int32_t* /*_aidl_return*/) {
// TODO(hkuang): Add implementation.
return Status::ok();
}
@@ -79,11 +188,11 @@
return Status::ok();
}
-Status MediaTranscodingService::getJobWithId(int32_t /*in_jobId*/, TranscodingJob* /*out_job*/,
+Status MediaTranscodingService::getJobWithId(int32_t /*in_jobId*/,
+ TranscodingJobParcel* /*out_job*/,
bool* /*_aidl_return*/) {
// TODO(hkuang): Add implementation.
return Status::ok();
}
-} // namespace media
} // namespace android
diff --git a/services/mediatranscoding/MediaTranscodingService.h b/services/mediatranscoding/MediaTranscodingService.h
index c2c30b9..cc69727 100644
--- a/services/mediatranscoding/MediaTranscodingService.h
+++ b/services/mediatranscoding/MediaTranscodingService.h
@@ -19,19 +19,21 @@
#include <aidl/android/media/BnMediaTranscodingService.h>
#include <binder/IServiceManager.h>
+#include <media/TranscodingClientManager.h>
namespace android {
-namespace media {
-
using Status = ::ndk::ScopedAStatus;
using ::aidl::android::media::BnMediaTranscodingService;
using ::aidl::android::media::ITranscodingServiceClient;
-using ::aidl::android::media::TranscodingJob;
-using ::aidl::android::media::TranscodingRequest;
+using ::aidl::android::media::TranscodingJobParcel;
+using ::aidl::android::media::TranscodingRequestParcel;
class MediaTranscodingService : public BnMediaTranscodingService {
public:
+ static constexpr int32_t kInvalidJobId = -1;
+ static constexpr int32_t kInvalidClientId = -1;
+
MediaTranscodingService();
virtual ~MediaTranscodingService();
@@ -40,23 +42,31 @@
static const char* getServiceName() { return "media.transcoding"; }
Status registerClient(const std::shared_ptr<ITranscodingServiceClient>& in_client,
- int32_t* _aidl_return) override;
+ const std::string& in_opPackageName, int32_t in_clientUid,
+ int32_t in_clientPid, int32_t* _aidl_return) override;
Status unregisterClient(int32_t clientId, bool* _aidl_return) override;
- Status submitRequest(int32_t in_clientId, const TranscodingRequest& in_request,
- TranscodingJob* out_job, int32_t* _aidl_return) override;
+ Status getNumOfClients(int32_t* _aidl_return) override;
+
+ Status submitRequest(int32_t in_clientId, const TranscodingRequestParcel& in_request,
+ TranscodingJobParcel* out_job, int32_t* _aidl_return) override;
Status cancelJob(int32_t in_clientId, int32_t in_jobId, bool* _aidl_return) override;
- Status getJobWithId(int32_t in_jobId, TranscodingJob* out_job, bool* _aidl_return) override;
+ Status getJobWithId(int32_t in_jobId, TranscodingJobParcel* out_job,
+ bool* _aidl_return) override;
virtual inline binder_status_t dump(int /*fd*/, const char** /*args*/, uint32_t /*numArgs*/);
private:
+ friend class MediaTranscodingServiceTest;
+
+ mutable std::mutex mServiceLock;
+
+ TranscodingClientManager& mTranscodingClientManager;
};
-} // namespace media
} // namespace android
#endif // ANDROID_MEDIA_TRANSCODING_SERVICE_H
diff --git a/services/mediatranscoding/main_mediatranscodingservice.cpp b/services/mediatranscoding/main_mediatranscodingservice.cpp
index 1978eb4..7d862e6 100644
--- a/services/mediatranscoding/main_mediatranscodingservice.cpp
+++ b/services/mediatranscoding/main_mediatranscodingservice.cpp
@@ -29,7 +29,7 @@
strcpy(argv[0], "media.transcoding");
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm = defaultServiceManager();
- android::media::MediaTranscodingService::instantiate();
+ android::MediaTranscodingService::instantiate();
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
diff --git a/services/mediatranscoding/tests/Android.bp b/services/mediatranscoding/tests/Android.bp
new file mode 100644
index 0000000..e0e040c
--- /dev/null
+++ b/services/mediatranscoding/tests/Android.bp
@@ -0,0 +1,35 @@
+// Build the unit tests for MediaTranscodingService
+
+cc_defaults {
+ name: "mediatranscodingservice_test_defaults",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+
+ include_dirs: [
+ "frameworks/av/services/mediatranscoding",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "libbinder_ndk",
+ "liblog",
+ "libutils",
+ "libmediatranscodingservice",
+ ],
+
+ static_libs: [
+ "mediatranscoding_aidl_interface-ndk_platform",
+ ],
+}
+
+// MediaTranscodingService unit test
+cc_test {
+ name: "mediatranscodingservice_tests",
+ defaults: ["mediatranscodingservice_test_defaults"],
+
+ srcs: ["mediatranscodingservice_tests.cpp"],
+}
\ No newline at end of file
diff --git a/services/mediatranscoding/tests/build_and_run_all_unit_tests.sh b/services/mediatranscoding/tests/build_and_run_all_unit_tests.sh
new file mode 100644
index 0000000..bcdc7f7
--- /dev/null
+++ b/services/mediatranscoding/tests/build_and_run_all_unit_tests.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Run tests in this directory.
+#
+
+if [ -z "$ANDROID_BUILD_TOP" ]; then
+ echo "Android build environment not set"
+ exit -1
+fi
+
+# ensure we have mm
+. $ANDROID_BUILD_TOP/build/envsetup.sh
+
+mm
+
+echo "waiting for device"
+
+adb root && adb wait-for-device remount && adb sync
+
+echo "========================================"
+
+echo "testing mediatranscodingservice"
+adb shell /data/nativetest64/mediatranscodingservice_tests/mediatranscodingservice_tests
diff --git a/services/mediatranscoding/tests/mediatranscodingservice_tests.cpp b/services/mediatranscoding/tests/mediatranscodingservice_tests.cpp
new file mode 100644
index 0000000..5a791fe
--- /dev/null
+++ b/services/mediatranscoding/tests/mediatranscodingservice_tests.cpp
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+// Unit Test for MediaTranscoding Service.
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaTranscodingServiceTest"
+
+#include <aidl/android/media/BnTranscodingServiceClient.h>
+#include <aidl/android/media/IMediaTranscodingService.h>
+#include <aidl/android/media/ITranscodingServiceClient.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <android/binder_ibinder_jni.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <cutils/ashmem.h>
+#include <gtest/gtest.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <utils/Log.h>
+
+namespace android {
+
+namespace media {
+
+using Status = ::ndk::ScopedAStatus;
+using aidl::android::media::BnTranscodingServiceClient;
+using aidl::android::media::IMediaTranscodingService;
+using aidl::android::media::ITranscodingServiceClient;
+
+constexpr int32_t kInvalidClientId = -5;
+
+// Note that -1 is valid and means using calling pid/uid for the service. But only privilege caller could
+// use them. This test is not a privilege caller.
+constexpr int32_t kInvalidClientPid = -5;
+constexpr int32_t kInvalidClientUid = -5;
+constexpr const char* kInvalidClientOpPackageName = "";
+
+constexpr int32_t kClientUseCallingPid = -1;
+constexpr int32_t kClientUseCallingUid = -1;
+constexpr const char* kClientOpPackageName = "TestClient";
+
+class MediaTranscodingServiceTest : public ::testing::Test {
+public:
+ MediaTranscodingServiceTest() { ALOGD("MediaTranscodingServiceTest created"); }
+
+ void SetUp() override {
+ ::ndk::SpAIBinder binder(AServiceManager_getService("media.transcoding"));
+ mService = IMediaTranscodingService::fromBinder(binder);
+ if (mService == nullptr) {
+ ALOGE("Failed to connect to the media.trascoding service.");
+ return;
+ }
+ }
+
+ ~MediaTranscodingServiceTest() { ALOGD("MediaTranscodingingServiceTest destroyed"); }
+
+ std::shared_ptr<IMediaTranscodingService> mService = nullptr;
+};
+
+struct TestClient : public BnTranscodingServiceClient {
+ TestClient(const std::shared_ptr<IMediaTranscodingService>& service) : mService(service) {
+ ALOGD("TestClient Created");
+ }
+
+ Status getName(std::string* _aidl_return) override {
+ *_aidl_return = "test_client";
+ return Status::ok();
+ }
+
+ Status onTranscodingFinished(
+ int32_t /* in_jobId */,
+ const ::aidl::android::media::TranscodingResultParcel& /* in_result */) override {
+ return Status::ok();
+ }
+
+ Status onTranscodingFailed(
+ int32_t /* in_jobId */,
+ ::aidl::android::media::TranscodingErrorCode /*in_errorCode */) override {
+ return Status::ok();
+ }
+
+ Status onAwaitNumberOfJobsChanged(int32_t /* in_jobId */, int32_t /* in_oldAwaitNumber */,
+ int32_t /* in_newAwaitNumber */) override {
+ return Status::ok();
+ }
+
+ Status onProgressUpdate(int32_t /* in_jobId */, int32_t /* in_progress */) override {
+ return Status::ok();
+ }
+
+ virtual ~TestClient() { ALOGI("TestClient destroyed"); };
+
+private:
+ std::shared_ptr<IMediaTranscodingService> mService;
+};
+
+TEST_F(MediaTranscodingServiceTest, TestRegisterNullClient) {
+ std::shared_ptr<ITranscodingServiceClient> client = nullptr;
+ int32_t clientId = 0;
+ Status status = mService->registerClient(client, kClientOpPackageName, kClientUseCallingUid,
+ kClientUseCallingPid, &clientId);
+ EXPECT_FALSE(status.isOk());
+}
+
+TEST_F(MediaTranscodingServiceTest, TestRegisterClientWithInvalidClientPid) {
+ std::shared_ptr<ITranscodingServiceClient> client =
+ ::ndk::SharedRefBase::make<TestClient>(mService);
+ EXPECT_TRUE(client != nullptr);
+
+ // Register the client with the service.
+ int32_t clientId = 0;
+ Status status = mService->registerClient(client, kClientOpPackageName, kClientUseCallingUid,
+ kInvalidClientPid, &clientId);
+ EXPECT_FALSE(status.isOk());
+}
+
+TEST_F(MediaTranscodingServiceTest, TestRegisterClientWithInvalidClientUid) {
+ std::shared_ptr<ITranscodingServiceClient> client =
+ ::ndk::SharedRefBase::make<TestClient>(mService);
+ EXPECT_TRUE(client != nullptr);
+
+ // Register the client with the service.
+ int32_t clientId = 0;
+ Status status = mService->registerClient(client, kClientOpPackageName, kInvalidClientUid,
+ kClientUseCallingPid, &clientId);
+ EXPECT_FALSE(status.isOk());
+}
+
+TEST_F(MediaTranscodingServiceTest, TestRegisterClientWithInvalidClientPackageName) {
+ std::shared_ptr<ITranscodingServiceClient> client =
+ ::ndk::SharedRefBase::make<TestClient>(mService);
+ EXPECT_TRUE(client != nullptr);
+
+ // Register the client with the service.
+ int32_t clientId = 0;
+ Status status = mService->registerClient(client, kInvalidClientOpPackageName,
+ kClientUseCallingUid, kClientUseCallingPid, &clientId);
+ EXPECT_FALSE(status.isOk());
+}
+
+TEST_F(MediaTranscodingServiceTest, TestRegisterOneClient) {
+ std::shared_ptr<ITranscodingServiceClient> client =
+ ::ndk::SharedRefBase::make<TestClient>(mService);
+ EXPECT_TRUE(client != nullptr);
+
+ // Register the client with the service.
+ int32_t clientId = 0;
+ Status status = mService->registerClient(client, kClientOpPackageName, kClientUseCallingPid,
+ kClientUseCallingUid, &clientId);
+ ALOGD("client id is %d", clientId);
+ EXPECT_TRUE(status.isOk());
+
+ // Validate the clientId.
+ EXPECT_TRUE(clientId > 0);
+
+ // Check the number of Clients.
+ int32_t numOfClients;
+ status = mService->getNumOfClients(&numOfClients);
+ EXPECT_TRUE(status.isOk());
+ EXPECT_EQ(1, numOfClients);
+
+ // Unregister the client.
+ bool res;
+ status = mService->unregisterClient(clientId, &res);
+ EXPECT_TRUE(status.isOk());
+ EXPECT_TRUE(res);
+}
+
+TEST_F(MediaTranscodingServiceTest, TestUnRegisterClientWithInvalidClientId) {
+ std::shared_ptr<ITranscodingServiceClient> client =
+ ::ndk::SharedRefBase::make<TestClient>(mService);
+ EXPECT_TRUE(client != nullptr);
+
+ // Register the client with the service.
+ int32_t clientId = 0;
+ Status status = mService->registerClient(client, kClientOpPackageName, kClientUseCallingUid,
+ kClientUseCallingPid, &clientId);
+ ALOGD("client id is %d", clientId);
+ EXPECT_TRUE(status.isOk());
+
+ // Validate the clientId.
+ EXPECT_TRUE(clientId > 0);
+
+ // Check the number of Clients.
+ int32_t numOfClients;
+ status = mService->getNumOfClients(&numOfClients);
+ EXPECT_TRUE(status.isOk());
+ EXPECT_EQ(1, numOfClients);
+
+ // Unregister the client with invalid ID
+ bool res;
+ mService->unregisterClient(kInvalidClientId, &res);
+ EXPECT_FALSE(res);
+
+ // Unregister the valid client.
+ mService->unregisterClient(clientId, &res);
+}
+
+TEST_F(MediaTranscodingServiceTest, TestRegisterClientTwice) {
+ std::shared_ptr<ITranscodingServiceClient> client =
+ ::ndk::SharedRefBase::make<TestClient>(mService);
+ EXPECT_TRUE(client != nullptr);
+
+ // Register the client with the service.
+ int32_t clientId = 0;
+ Status status = mService->registerClient(client, kClientOpPackageName, kClientUseCallingUid,
+ kClientUseCallingPid, &clientId);
+ EXPECT_TRUE(status.isOk());
+
+ // Validate the clientId.
+ EXPECT_TRUE(clientId > 0);
+
+ // Register the client again and expects failure.
+ status = mService->registerClient(client, kClientOpPackageName, kClientUseCallingUid,
+ kClientUseCallingPid, &clientId);
+ EXPECT_FALSE(status.isOk());
+
+ // Unregister the valid client.
+ bool res;
+ mService->unregisterClient(clientId, &res);
+}
+
+} // namespace media
+} // namespace android
diff --git a/services/minijail/Android.bp b/services/minijail/Android.bp
index 07a94cc..0713a87 100644
--- a/services/minijail/Android.bp
+++ b/services/minijail/Android.bp
@@ -17,10 +17,14 @@
cc_library_shared {
name: "libavservices_minijail",
defaults: ["libavservices_minijail_defaults"],
+ vendor_available: true,
export_include_dirs: ["."],
}
-// Small library for media.extractor and media.codec sandboxing.
+// By adding "vendor_available: true" to "libavservices_minijail", we don't
+// need to have "libavservices_minijail_vendor" any longer.
+// "libavservices_minijail_vendor" will be removed, once we replace it with
+// "libavservices_minijail" in all vendor modules. (b/146313710)
cc_library_shared {
name: "libavservices_minijail_vendor",
vendor: true,
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index a1fc0ea..82cc90e 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -284,7 +284,7 @@
serviceEndpoint->close();
mSharedCloseCount++;
- ALOGV("%s() %p for device %d",
+ ALOGV("%s(%p) closed for device %d",
__func__, serviceEndpoint.get(), serviceEndpoint->getDeviceId());
}
}
diff --git a/services/oboeservice/AAudioServiceEndpointShared.cpp b/services/oboeservice/AAudioServiceEndpointShared.cpp
index 0a415fd..9b3b3b8 100644
--- a/services/oboeservice/AAudioServiceEndpointShared.cpp
+++ b/services/oboeservice/AAudioServiceEndpointShared.cpp
@@ -85,7 +85,7 @@
}
aaudio_result_t AAudioServiceEndpointShared::close() {
- return getStreamInternal()->close();
+ return getStreamInternal()->releaseCloseFinal();
}
// Glue between C and C++ callbacks.
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index 880a3d7..39e90b1 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -56,7 +56,8 @@
LOG_ALWAYS_FATAL_IF(!(getState() == AAUDIO_STREAM_STATE_CLOSED
|| getState() == AAUDIO_STREAM_STATE_UNINITIALIZED
|| getState() == AAUDIO_STREAM_STATE_DISCONNECTED),
- "service stream still open, state = %d", getState());
+ "service stream %p still open, state = %d",
+ this, getState());
}
std::string AAudioServiceStreamBase::dumpHeader() {
@@ -126,13 +127,13 @@
}
aaudio_result_t AAudioServiceStreamBase::close() {
- aaudio_result_t result = AAUDIO_OK;
if (getState() == AAUDIO_STREAM_STATE_CLOSED) {
return AAUDIO_OK;
}
stop();
+ aaudio_result_t result = AAUDIO_OK;
sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
if (endpoint == nullptr) {
result = AAUDIO_ERROR_INVALID_STATE;
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index 837b080..f4e72b7 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -49,16 +49,6 @@
, mInService(inService) {
}
-aaudio_result_t AAudioServiceStreamMMAP::close() {
- if (getState() == AAUDIO_STREAM_STATE_CLOSED) {
- return AAUDIO_OK;
- }
-
- stop();
-
- return AAudioServiceStreamBase::close();
-}
-
// Open stream on HAL and pass information about the shared memory buffer back to the client.
aaudio_result_t AAudioServiceStreamMMAP::open(const aaudio::AAudioStreamRequest &request) {
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.h b/services/oboeservice/AAudioServiceStreamMMAP.h
index 1509f7d..3d56623 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.h
+++ b/services/oboeservice/AAudioServiceStreamMMAP.h
@@ -67,8 +67,6 @@
aaudio_result_t stopClient(audio_port_handle_t clientHandle) override;
- aaudio_result_t close() override;
-
const char *getTypeText() const override { return "MMAP"; }
protected:
diff --git a/services/oboeservice/Android.bp b/services/oboeservice/Android.bp
index ca1354d..64d835b 100644
--- a/services/oboeservice/Android.bp
+++ b/services/oboeservice/Android.bp
@@ -55,4 +55,12 @@
"libutils",
],
+ header_libs: [
+ "libaudiohal_headers",
+ ],
+
+ include_dirs: [
+ "frameworks/av/media/libnbaio/include_mono",
+ "frameworks/av/media/libnbaio/include",
+ ],
}
diff --git a/services/soundtrigger/Android.bp b/services/soundtrigger/Android.bp
deleted file mode 100644
index 600f4a3..0000000
--- a/services/soundtrigger/Android.bp
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2014 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.
-
-cc_library_shared {
- name: "libsoundtriggerservice",
-
- srcs: [
- "SoundTriggerHwService.cpp",
- "SoundTriggerHalHidl.cpp",
- ],
-
- shared_libs: [
- "liblog",
- "libutils",
- "libbinder",
- "libcutils",
- "libhardware",
- "libsoundtrigger",
- "libaudioclient",
- "libaudioutils",
- "libmediautils",
-
- "libhidlbase",
- "libhidlmemory",
- "libbase",
- "libaudiohal",
- "libaudiohal_deathhandler",
- "android.hardware.soundtrigger@2.0",
- "android.hardware.soundtrigger@2.1",
- "android.hardware.soundtrigger@2.2",
- "android.hardware.soundtrigger@2.3",
- "android.hardware.audio.common@2.0",
- "android.hidl.allocator@1.0",
- "android.hidl.memory@1.0",
- ],
-
- include_dirs: ["frameworks/av/services/audioflinger"],
-
- cflags: [
- "-Wall",
- "-Werror",
- ],
-}
diff --git a/services/soundtrigger/OWNERS b/services/soundtrigger/OWNERS
deleted file mode 100644
index e83f6b9..0000000
--- a/services/soundtrigger/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-elaurent@google.com
-thorntonc@google.com
diff --git a/services/soundtrigger/SoundTriggerHalHidl.cpp b/services/soundtrigger/SoundTriggerHalHidl.cpp
deleted file mode 100644
index b1d34df..0000000
--- a/services/soundtrigger/SoundTriggerHalHidl.cpp
+++ /dev/null
@@ -1,999 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "SoundTriggerHalHidl"
-//#define LOG_NDEBUG 0
-
-#include <android/hidl/allocator/1.0/IAllocator.h>
-#include <media/audiohal/hidl/HalDeathHandler.h>
-#include <utils/Log.h>
-#include "SoundTriggerHalHidl.h"
-#include <hidlmemory/mapping.h>
-#include <hwbinder/IPCThreadState.h>
-#include <hwbinder/ProcessState.h>
-
-namespace android {
-
-using ::android::hardware::ProcessState;
-using ::android::hardware::Return;
-using ::android::hardware::Status;
-using ::android::hardware::Void;
-using ::android::hardware::audio::common::V2_0::AudioDevice;
-using ::android::hardware::hidl_memory;
-using ::android::hidl::allocator::V1_0::IAllocator;
-using ::android::hidl::memory::V1_0::IMemory;
-
-namespace {
-
-// Backs up by the vector with the contents of shared memory.
-// It is assumed that the passed hidl_vector is empty, so it's
-// not cleared if the memory is a null object.
-// The caller needs to keep the returned sp<IMemory> as long as
-// the data is needed.
-std::pair<bool, sp<IMemory>> memoryAsVector(const hidl_memory& m, hidl_vec<uint8_t>* vec) {
- sp<IMemory> memory;
- if (m.size() == 0) {
- return std::make_pair(true, memory);
- }
- memory = mapMemory(m);
- if (memory != nullptr) {
- memory->read();
- vec->setToExternal(static_cast<uint8_t*>(static_cast<void*>(memory->getPointer())),
- memory->getSize());
- return std::make_pair(true, memory);
- }
- ALOGE("%s: Could not map HIDL memory to IMemory", __func__);
- return std::make_pair(false, memory);
-}
-
-// Moves the data from the vector into allocated shared memory,
-// emptying the vector.
-// It is assumed that the passed hidl_memory is a null object, so it's
-// not reset if the vector is empty.
-// The caller needs to keep the returned sp<IMemory> as long as
-// the data is needed.
-std::pair<bool, sp<IMemory>> moveVectorToMemory(hidl_vec<uint8_t>* v, hidl_memory* mem) {
- sp<IMemory> memory;
- if (v->size() == 0) {
- return std::make_pair(true, memory);
- }
- sp<IAllocator> ashmem = IAllocator::getService("ashmem");
- if (ashmem == 0) {
- ALOGE("Failed to retrieve ashmem allocator service");
- return std::make_pair(false, memory);
- }
- bool success = false;
- Return<void> r = ashmem->allocate(v->size(), [&](bool s, const hidl_memory& m) {
- success = s;
- if (success) *mem = m;
- });
- if (r.isOk() && success) {
- memory = hardware::mapMemory(*mem);
- if (memory != 0) {
- memory->update();
- memcpy(memory->getPointer(), v->data(), v->size());
- memory->commit();
- v->resize(0);
- return std::make_pair(true, memory);
- } else {
- ALOGE("Failed to map allocated ashmem");
- }
- } else {
- ALOGE("Failed to allocate %llu bytes from ashmem", (unsigned long long)v->size());
- }
- return std::make_pair(false, memory);
-}
-
-} // namespace
-
-/* static */
-sp<SoundTriggerHalInterface> SoundTriggerHalInterface::connectModule(const char *moduleName)
-{
- return new SoundTriggerHalHidl(moduleName);
-}
-
-int SoundTriggerHalHidl::getProperties(struct sound_trigger_properties *properties)
-{
- sp<ISoundTriggerHw> soundtrigger = getService();
- if (soundtrigger == 0) {
- return -ENODEV;
- }
-
- ISoundTriggerHw::Properties halProperties;
- Return<void> hidlReturn;
- int ret;
- {
- AutoMutex lock(mHalLock);
- hidlReturn = soundtrigger->getProperties([&](int rc, auto res) {
- ret = rc;
- halProperties = res;
- ALOGI("getProperties res implementor %s", res.implementor.c_str());
- });
- }
-
- if (hidlReturn.isOk()) {
- if (ret == 0) {
- convertPropertiesFromHal(properties, &halProperties);
- }
- } else {
- ALOGE("getProperties error %s", hidlReturn.description().c_str());
- return FAILED_TRANSACTION;
- }
- ALOGI("getProperties ret %d", ret);
- return ret;
-}
-
-int SoundTriggerHalHidl::loadSoundModel(struct sound_trigger_sound_model *sound_model,
- sound_model_callback_t callback,
- void *cookie,
- sound_model_handle_t *handle)
-{
- if (handle == NULL) {
- return -EINVAL;
- }
-
- sp<ISoundTriggerHw> soundtrigger = getService();
- if (soundtrigger == 0) {
- return -ENODEV;
- }
-
- uint32_t modelId;
- {
- AutoMutex lock(mLock);
- do {
- modelId = nextUniqueId();
- ALOGI("loadSoundModel modelId %u", modelId);
- sp<SoundModel> model = mSoundModels.valueFor(modelId);
- ALOGI("loadSoundModel model %p", model.get());
- } while (mSoundModels.valueFor(modelId) != 0 && modelId != 0);
- }
- LOG_ALWAYS_FATAL_IF(modelId == 0,
- "loadSoundModel(): wrap around in sound model IDs, num loaded models %zd",
- mSoundModels.size());
-
- Return<void> hidlReturn;
- int ret;
- SoundModelHandle halHandle;
- sp<V2_1_ISoundTriggerHw> soundtrigger_2_1 = toService2_1(soundtrigger);
- sp<V2_2_ISoundTriggerHw> soundtrigger_2_2 = toService2_2(soundtrigger);
- if (sound_model->type == SOUND_MODEL_TYPE_KEYPHRASE) {
- if (soundtrigger_2_2) {
- V2_2_ISoundTriggerHw::PhraseSoundModel halSoundModel;
- auto result = convertPhraseSoundModelToHal(&halSoundModel, sound_model);
- if (result.first) {
- AutoMutex lock(mHalLock);
- hidlReturn = soundtrigger_2_2->loadPhraseSoundModel_2_1(
- halSoundModel,
- this, modelId, [&](int32_t retval, auto res) {
- ret = retval;
- halHandle = res;
- });
- } else {
- return NO_MEMORY;
- }
- } else if (soundtrigger_2_1) {
- V2_1_ISoundTriggerHw::PhraseSoundModel halSoundModel;
- auto result = convertPhraseSoundModelToHal(&halSoundModel, sound_model);
- if (result.first) {
- AutoMutex lock(mHalLock);
- hidlReturn = soundtrigger_2_1->loadPhraseSoundModel_2_1(
- halSoundModel,
- this, modelId, [&](int32_t retval, auto res) {
- ret = retval;
- halHandle = res;
- });
- } else {
- return NO_MEMORY;
- }
- } else {
- ISoundTriggerHw::PhraseSoundModel halSoundModel;
- convertPhraseSoundModelToHal(&halSoundModel, sound_model);
- AutoMutex lock(mHalLock);
- hidlReturn = soundtrigger->loadPhraseSoundModel(
- halSoundModel,
- this, modelId, [&](int32_t retval, auto res) {
- ret = retval;
- halHandle = res;
- });
- }
- } else {
- if (soundtrigger_2_2) {
- V2_2_ISoundTriggerHw::SoundModel halSoundModel;
- auto result = convertSoundModelToHal(&halSoundModel, sound_model);
- if (result.first) {
- AutoMutex lock(mHalLock);
- hidlReturn = soundtrigger_2_2->loadSoundModel_2_1(halSoundModel,
- this, modelId, [&](int32_t retval, auto res) {
- ret = retval;
- halHandle = res;
- });
- } else {
- return NO_MEMORY;
- }
- } else if (soundtrigger_2_1) {
- V2_1_ISoundTriggerHw::SoundModel halSoundModel;
- auto result = convertSoundModelToHal(&halSoundModel, sound_model);
- if (result.first) {
- AutoMutex lock(mHalLock);
- hidlReturn = soundtrigger_2_1->loadSoundModel_2_1(halSoundModel,
- this, modelId, [&](int32_t retval, auto res) {
- ret = retval;
- halHandle = res;
- });
- } else {
- return NO_MEMORY;
- }
- } else {
- ISoundTriggerHw::SoundModel halSoundModel;
- convertSoundModelToHal(&halSoundModel, sound_model);
- AutoMutex lock(mHalLock);
- hidlReturn = soundtrigger->loadSoundModel(halSoundModel,
- this, modelId, [&](int32_t retval, auto res) {
- ret = retval;
- halHandle = res;
- });
- }
- }
-
- if (hidlReturn.isOk()) {
- if (ret == 0) {
- AutoMutex lock(mLock);
- *handle = (sound_model_handle_t)modelId;
- sp<SoundModel> model = new SoundModel(*handle, callback, cookie, halHandle);
- mSoundModels.add(*handle, model);
- }
- } else {
- ALOGE("loadSoundModel error %s", hidlReturn.description().c_str());
- return FAILED_TRANSACTION;
- }
-
- return ret;
-}
-
-int SoundTriggerHalHidl::unloadSoundModel(sound_model_handle_t handle)
-{
- sp<ISoundTriggerHw> soundtrigger = getService();
- if (soundtrigger == 0) {
- return -ENODEV;
- }
-
- sp<SoundModel> model = removeModel(handle);
- if (model == 0) {
- ALOGE("unloadSoundModel model not found for handle %u", handle);
- return -EINVAL;
- }
-
- Return<int32_t> hidlReturn(0);
- {
- AutoMutex lock(mHalLock);
- hidlReturn = soundtrigger->unloadSoundModel(model->mHalHandle);
- }
-
- if (!hidlReturn.isOk()) {
- ALOGE("unloadSoundModel error %s", hidlReturn.description().c_str());
- return FAILED_TRANSACTION;
- }
-
- return hidlReturn;
-}
-
-int SoundTriggerHalHidl::startRecognition(sound_model_handle_t handle,
- const struct sound_trigger_recognition_config *config,
- recognition_callback_t callback,
- void *cookie)
-{
- sp<ISoundTriggerHw> soundtrigger = getService();
- if (soundtrigger == 0) {
- return -ENODEV;
- }
-
- sp<SoundModel> model = getModel(handle);
- if (model == 0) {
- ALOGE("startRecognition model not found for handle %u", handle);
- return -EINVAL;
- }
-
- model->mRecognitionCallback = callback;
- model->mRecognitionCookie = cookie;
-
- sp<V2_1_ISoundTriggerHw> soundtrigger_2_1 = toService2_1(soundtrigger);
- sp<V2_2_ISoundTriggerHw> soundtrigger_2_2 = toService2_2(soundtrigger);
- Return<int32_t> hidlReturn(0);
-
- if (soundtrigger_2_2) {
- V2_2_ISoundTriggerHw::RecognitionConfig halConfig;
- auto result = convertRecognitionConfigToHal(&halConfig, config);
- if (result.first) {
- AutoMutex lock(mHalLock);
- hidlReturn = soundtrigger_2_2->startRecognition_2_1(
- model->mHalHandle, halConfig, this, handle);
- } else {
- return NO_MEMORY;
- }
- } else if (soundtrigger_2_1) {
- V2_1_ISoundTriggerHw::RecognitionConfig halConfig;
- auto result = convertRecognitionConfigToHal(&halConfig, config);
- if (result.first) {
- AutoMutex lock(mHalLock);
- hidlReturn = soundtrigger_2_1->startRecognition_2_1(
- model->mHalHandle, halConfig, this, handle);
- } else {
- return NO_MEMORY;
- }
- } else {
- ISoundTriggerHw::RecognitionConfig halConfig;
- convertRecognitionConfigToHal(&halConfig, config);
- {
- AutoMutex lock(mHalLock);
- hidlReturn = soundtrigger->startRecognition(model->mHalHandle, halConfig, this, handle);
- }
- }
-
- if (!hidlReturn.isOk()) {
- ALOGE("startRecognition error %s", hidlReturn.description().c_str());
- return FAILED_TRANSACTION;
- }
- return hidlReturn;
-}
-
-int SoundTriggerHalHidl::stopRecognition(sound_model_handle_t handle)
-{
- sp<ISoundTriggerHw> soundtrigger = getService();
- if (soundtrigger == 0) {
- return -ENODEV;
- }
-
- sp<SoundModel> model = getModel(handle);
- if (model == 0) {
- ALOGE("stopRecognition model not found for handle %u", handle);
- return -EINVAL;
- }
-
- Return<int32_t> hidlReturn(0);
- {
- AutoMutex lock(mHalLock);
- hidlReturn = soundtrigger->stopRecognition(model->mHalHandle);
- }
-
- if (!hidlReturn.isOk()) {
- ALOGE("stopRecognition error %s", hidlReturn.description().c_str());
- return FAILED_TRANSACTION;
- }
- return hidlReturn;
-}
-
-int SoundTriggerHalHidl::stopAllRecognitions()
-{
- sp<ISoundTriggerHw> soundtrigger = getService();
- if (soundtrigger == 0) {
- return -ENODEV;
- }
-
- Return<int32_t> hidlReturn(0);
- {
- AutoMutex lock(mHalLock);
- hidlReturn = soundtrigger->stopAllRecognitions();
- }
-
- if (!hidlReturn.isOk()) {
- ALOGE("stopAllRecognitions error %s", hidlReturn.description().c_str());
- return FAILED_TRANSACTION;
- }
- return hidlReturn;
-}
-
-int SoundTriggerHalHidl::getModelState(sound_model_handle_t handle)
-{
- sp<ISoundTriggerHw> soundtrigger = getService();
- if (soundtrigger == 0) {
- return -ENODEV;
- }
-
- sp<V2_2_ISoundTriggerHw> soundtrigger_2_2 = toService2_2(soundtrigger);
- if (soundtrigger_2_2 == 0) {
- ALOGE("getModelState not supported");
- return -ENODEV;
- }
-
- sp<SoundModel> model = getModel(handle);
- if (model == 0) {
- ALOGE("getModelState model not found for handle %u", handle);
- return -EINVAL;
- }
-
- int ret = NO_ERROR;
- Return<int32_t> hidlReturn(0);
- {
- AutoMutex lock(mHalLock);
- hidlReturn = soundtrigger_2_2->getModelState(model->mHalHandle);
- }
- if (!hidlReturn.isOk()) {
- ALOGE("getModelState error %s", hidlReturn.description().c_str());
- ret = FAILED_TRANSACTION;
- }
- return ret;
-}
-
-int SoundTriggerHalHidl::setParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t model_param, int32_t value)
-{
- sp<ISoundTriggerHw> soundtrigger = getService();
- if (!soundtrigger) {
- return -ENODEV;
- }
-
- sp<V2_3_ISoundTriggerHw> soundtrigger_2_3 = toService2_3(soundtrigger);
- if (!soundtrigger_2_3) {
- ALOGE("setParameter not supported");
- return -ENOSYS;
- }
-
- sp<SoundModel> model = getModel(handle);
- if (!model) {
- ALOGE("setParameter model not found for handle %u", handle);
- return -EINVAL;
- }
-
- V2_3_ModelParameter halParam;
- convertModelParameterToHal(&halParam, model_param);
-
- Return<int32_t> hidlReturn(0);
- {
- AutoMutex lock(mHalLock);
- hidlReturn = soundtrigger_2_3->setParameter(model->mHalHandle, halParam, value);
- }
- if (!hidlReturn.isOk()) {
- ALOGE("getModelState error %s", hidlReturn.description().c_str());
- return FAILED_TRANSACTION;
- }
-
- return hidlReturn;
-}
-
-int SoundTriggerHalHidl::getParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t model_param, int32_t* value)
-{
- sp<ISoundTriggerHw> soundtrigger = getService();
- if (!soundtrigger) {
- return -ENODEV;
- }
-
- sp<V2_3_ISoundTriggerHw> soundtrigger_2_3 = toService2_3(soundtrigger);
- if (!soundtrigger_2_3) {
- ALOGE("getParameter not supported");
- return -ENOSYS;
- }
-
- if (value == NULL) {
- ALOGE("getParameter invalid value pointer");
- return -EINVAL;
- }
-
- sp<SoundModel> model = getModel(handle);
- if (!model) {
- ALOGE("getParameter model not found for handle %u", handle);
- return -EINVAL;
- }
-
- V2_3_ModelParameter halParam;
- convertModelParameterToHal(&halParam, model_param);
-
- Return<void> hidlReturn;
- int32_t hidlStatus;
- int32_t hidlValue;
- {
- AutoMutex lock(mHalLock);
- hidlReturn = soundtrigger_2_3->getParameter(model->mHalHandle, halParam,
- [&](int32_t retStatus, int32_t retValue) {
- hidlStatus = retStatus;
- hidlValue = retValue;
- });
- }
- if (!hidlReturn.isOk()) {
- ALOGE("getModelState error %s", hidlReturn.description().c_str());
- return FAILED_TRANSACTION;
- }
-
- *value = hidlValue;
- return hidlStatus;
-}
-
-int SoundTriggerHalHidl::queryParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t model_param,
- sound_trigger_model_parameter_range_t* param_range)
-{
- sp<ISoundTriggerHw> soundtrigger = getService();
- if (!soundtrigger) {
- return -ENODEV;
- }
-
- sp<V2_3_ISoundTriggerHw> soundtrigger_2_3 = toService2_3(soundtrigger);
- if (!soundtrigger_2_3) {
- ALOGE("queryParameter not supported");
- return -ENOSYS;
- }
-
- sp<SoundModel> model = getModel(handle);
- if (!model) {
- ALOGE("queryParameter model not found for handle %u", handle);
- return -EINVAL;
- }
-
- V2_3_ModelParameter halParam;
- convertModelParameterToHal(&halParam, model_param);
-
- Return<void> hidlReturn;
- int32_t hidlStatus;
- V2_3_OptionalModelParameterRange hidlValue;
- {
- AutoMutex lock(mHalLock);
- hidlReturn = soundtrigger_2_3->queryParameter(model->mHalHandle, halParam,
- [&](int32_t retStatus, V2_3_OptionalModelParameterRange retValue) {
- hidlStatus = retStatus;
- hidlValue = retValue;
- });
- }
- if (!hidlReturn.isOk()) {
- ALOGE("queryParameter error %s", hidlReturn.description().c_str());
- return FAILED_TRANSACTION;
- }
-
- if (hidlStatus != 0) {
- ALOGE("queryParameter error code: %d", hidlStatus);
- return hidlStatus;
- }
-
- if (hidlValue.getDiscriminator() ==
- V2_3_OptionalModelParameterRange::hidl_discriminator::noinit) {
- return -1;
- }
-
- param_range->start = hidlValue.range().start;
- param_range->end = hidlValue.range().end;
-
- return 0;
-}
-
-SoundTriggerHalHidl::SoundTriggerHalHidl(const char *moduleName)
- : mModuleName(moduleName), mNextUniqueId(1)
-{
- LOG_ALWAYS_FATAL_IF(strcmp(mModuleName, "primary") != 0,
- "Treble soundtrigger only supports primary module");
-}
-
-SoundTriggerHalHidl::~SoundTriggerHalHidl()
-{
-}
-
-sp<ISoundTriggerHw> SoundTriggerHalHidl::getService()
-{
- AutoMutex lock(mLock);
- if (mISoundTrigger == 0) {
- if (mModuleName == NULL) {
- mModuleName = "primary";
- }
- mISoundTrigger = ISoundTriggerHw::getService();
- if (mISoundTrigger != 0) {
- mISoundTrigger->linkToDeath(HalDeathHandler::getInstance(), 0 /*cookie*/);
- }
- }
- return mISoundTrigger;
-}
-
-sp<V2_1_ISoundTriggerHw> SoundTriggerHalHidl::toService2_1(const sp<ISoundTriggerHw>& s)
-{
- auto castResult_2_1 = V2_1_ISoundTriggerHw::castFrom(s);
- return castResult_2_1.isOk() ? static_cast<sp<V2_1_ISoundTriggerHw>>(castResult_2_1) : nullptr;
-}
-
-sp<V2_2_ISoundTriggerHw> SoundTriggerHalHidl::toService2_2(const sp<ISoundTriggerHw>& s)
-{
- auto castResult_2_2 = V2_2_ISoundTriggerHw::castFrom(s);
- return castResult_2_2.isOk() ? static_cast<sp<V2_2_ISoundTriggerHw>>(castResult_2_2) : nullptr;
-}
-
-sp<V2_3_ISoundTriggerHw> SoundTriggerHalHidl::toService2_3(const sp<ISoundTriggerHw>& s)
-{
- auto castResult_3_0 = V2_3_ISoundTriggerHw::castFrom(s);
- return castResult_3_0.isOk() ? static_cast<sp<V2_3_ISoundTriggerHw>>(castResult_3_0) : nullptr;
-}
-
-sp<SoundTriggerHalHidl::SoundModel> SoundTriggerHalHidl::getModel(sound_model_handle_t handle)
-{
- AutoMutex lock(mLock);
- return mSoundModels.valueFor(handle);
-}
-
-sp<SoundTriggerHalHidl::SoundModel> SoundTriggerHalHidl::removeModel(sound_model_handle_t handle)
-{
- AutoMutex lock(mLock);
- sp<SoundModel> model = mSoundModels.valueFor(handle);
- mSoundModels.removeItem(handle);
- return model;
-}
-
-uint32_t SoundTriggerHalHidl::nextUniqueId()
-{
- return (uint32_t) atomic_fetch_add_explicit(&mNextUniqueId,
- (uint_fast32_t) 1, memory_order_acq_rel);
-}
-
-void SoundTriggerHalHidl::convertUuidToHal(Uuid *halUuid,
- const sound_trigger_uuid_t *uuid)
-{
- halUuid->timeLow = uuid->timeLow;
- halUuid->timeMid = uuid->timeMid;
- halUuid->versionAndTimeHigh = uuid->timeHiAndVersion;
- halUuid->variantAndClockSeqHigh = uuid->clockSeq;
- memcpy(halUuid->node.data(), &uuid->node[0], sizeof(uuid->node));
-}
-
-void SoundTriggerHalHidl::convertUuidFromHal(sound_trigger_uuid_t *uuid,
- const Uuid *halUuid)
-{
- uuid->timeLow = halUuid->timeLow;
- uuid->timeMid = halUuid->timeMid;
- uuid->timeHiAndVersion = halUuid->versionAndTimeHigh;
- uuid->clockSeq = halUuid->variantAndClockSeqHigh;
- memcpy(&uuid->node[0], halUuid->node.data(), sizeof(uuid->node));
-}
-
-void SoundTriggerHalHidl::convertPropertiesFromHal(
- struct sound_trigger_properties *properties,
- const ISoundTriggerHw::Properties *halProperties)
-{
- strlcpy(properties->implementor,
- halProperties->implementor.c_str(), SOUND_TRIGGER_MAX_STRING_LEN);
- strlcpy(properties->description,
- halProperties->description.c_str(), SOUND_TRIGGER_MAX_STRING_LEN);
- properties->version = halProperties->version;
- convertUuidFromHal(&properties->uuid, &halProperties->uuid);
- properties->max_sound_models = halProperties->maxSoundModels;
- properties->max_key_phrases = halProperties->maxKeyPhrases;
- properties->max_users = halProperties->maxUsers;
- properties->recognition_modes = halProperties->recognitionModes;
- properties->capture_transition = (bool)halProperties->captureTransition;
- properties->max_buffer_ms = halProperties->maxBufferMs;
- properties->concurrent_capture = (bool)halProperties->concurrentCapture;
- properties->trigger_in_event = (bool)halProperties->triggerInEvent;
- properties->power_consumption_mw = halProperties->powerConsumptionMw;
-}
-
-// static
-void SoundTriggerHalHidl::convertModelParameterToHal(V2_3_ModelParameter* halParam,
- sound_trigger_model_parameter_t param)
-{
- switch (param) {
- case MODEL_PARAMETER_THRESHOLD_FACTOR:
- *halParam = V2_3_ModelParameter::THRESHOLD_FACTOR;
- return;
- case MODEL_PARAMETER_INVALID:
- default:
- *halParam = V2_3_ModelParameter::INVALID;
- }
-}
-
-void SoundTriggerHalHidl::convertTriggerPhraseToHal(
- ISoundTriggerHw::Phrase *halTriggerPhrase,
- const struct sound_trigger_phrase *triggerPhrase)
-{
- halTriggerPhrase->id = triggerPhrase->id;
- halTriggerPhrase->recognitionModes = triggerPhrase->recognition_mode;
- halTriggerPhrase->users.setToExternal((uint32_t *)&triggerPhrase->users[0], triggerPhrase->num_users);
- halTriggerPhrase->locale = triggerPhrase->locale;
- halTriggerPhrase->text = triggerPhrase->text;
-}
-
-
-void SoundTriggerHalHidl::convertTriggerPhrasesToHal(
- hidl_vec<ISoundTriggerHw::Phrase> *halTriggerPhrases,
- struct sound_trigger_phrase_sound_model *keyPhraseModel)
-{
- halTriggerPhrases->resize(keyPhraseModel->num_phrases);
- for (unsigned int i = 0; i < keyPhraseModel->num_phrases; i++) {
- convertTriggerPhraseToHal(&(*halTriggerPhrases)[i], &keyPhraseModel->phrases[i]);
- }
-}
-
-void SoundTriggerHalHidl::convertSoundModelToHal(ISoundTriggerHw::SoundModel *halModel,
- const struct sound_trigger_sound_model *soundModel)
-{
- halModel->type = (SoundModelType)soundModel->type;
- convertUuidToHal(&halModel->uuid, &soundModel->uuid);
- convertUuidToHal(&halModel->vendorUuid, &soundModel->vendor_uuid);
- halModel->data.setToExternal((uint8_t *)soundModel + soundModel->data_offset, soundModel->data_size);
-}
-
-std::pair<bool, sp<IMemory>> SoundTriggerHalHidl::convertSoundModelToHal(
- V2_1_ISoundTriggerHw::SoundModel *halModel,
- const struct sound_trigger_sound_model *soundModel)
-{
- convertSoundModelToHal(&halModel->header, soundModel);
- return moveVectorToMemory(&halModel->header.data, &halModel->data);
-}
-
-void SoundTriggerHalHidl::convertPhraseSoundModelToHal(
- ISoundTriggerHw::PhraseSoundModel *halKeyPhraseModel,
- const struct sound_trigger_sound_model *soundModel)
-{
- struct sound_trigger_phrase_sound_model *keyPhraseModel =
- (struct sound_trigger_phrase_sound_model *)soundModel;
- convertTriggerPhrasesToHal(&halKeyPhraseModel->phrases, keyPhraseModel);
- convertSoundModelToHal(&halKeyPhraseModel->common, soundModel);
-}
-
-std::pair<bool, sp<IMemory>> SoundTriggerHalHidl::convertPhraseSoundModelToHal(
- V2_1_ISoundTriggerHw::PhraseSoundModel *halKeyPhraseModel,
- const struct sound_trigger_sound_model *soundModel)
-{
- struct sound_trigger_phrase_sound_model *keyPhraseModel =
- (struct sound_trigger_phrase_sound_model *)soundModel;
- convertTriggerPhrasesToHal(&halKeyPhraseModel->phrases, keyPhraseModel);
- return convertSoundModelToHal(&halKeyPhraseModel->common, soundModel);
-}
-
-void SoundTriggerHalHidl::convertPhraseRecognitionExtraToHal(
- PhraseRecognitionExtra *halExtra,
- const struct sound_trigger_phrase_recognition_extra *extra)
-{
- halExtra->id = extra->id;
- halExtra->recognitionModes = extra->recognition_modes;
- halExtra->confidenceLevel = extra->confidence_level;
- halExtra->levels.resize(extra->num_levels);
- for (unsigned int i = 0; i < extra->num_levels; i++) {
- halExtra->levels[i].userId = extra->levels[i].user_id;
- halExtra->levels[i].levelPercent = extra->levels[i].level;
- }
-}
-
-void SoundTriggerHalHidl::convertRecognitionConfigToHal(
- ISoundTriggerHw::RecognitionConfig *halConfig,
- const struct sound_trigger_recognition_config *config)
-{
- halConfig->captureHandle = config->capture_handle;
- halConfig->captureDevice = (AudioDevice)config->capture_device;
- halConfig->captureRequested = (uint32_t)config->capture_requested;
-
- halConfig->phrases.resize(config->num_phrases);
- for (unsigned int i = 0; i < config->num_phrases; i++) {
- convertPhraseRecognitionExtraToHal(&halConfig->phrases[i],
- &config->phrases[i]);
- }
-
- halConfig->data.setToExternal((uint8_t *)config + config->data_offset, config->data_size);
-}
-
-std::pair<bool, sp<IMemory>> SoundTriggerHalHidl::convertRecognitionConfigToHal(
- V2_1_ISoundTriggerHw::RecognitionConfig *halConfig,
- const struct sound_trigger_recognition_config *config)
-{
- convertRecognitionConfigToHal(&halConfig->header, config);
- return moveVectorToMemory(&halConfig->header.data, &halConfig->data);
-}
-
-
-// ISoundTriggerHwCallback
-::android::hardware::Return<void> SoundTriggerHalHidl::recognitionCallback(
- const V2_0_ISoundTriggerHwCallback::RecognitionEvent& halEvent,
- CallbackCookie cookie)
-{
- sp<SoundModel> model;
- {
- AutoMutex lock(mLock);
- model = mSoundModels.valueFor((SoundModelHandle)cookie);
- if (model == 0) {
- return Return<void>();
- }
- }
- struct sound_trigger_recognition_event *event = convertRecognitionEventFromHal(&halEvent);
- if (event == NULL) {
- return Return<void>();
- }
- event->model = model->mHandle;
- model->mRecognitionCallback(event, model->mRecognitionCookie);
-
- free(event);
-
- return Return<void>();
-}
-
-::android::hardware::Return<void> SoundTriggerHalHidl::phraseRecognitionCallback(
- const V2_0_ISoundTriggerHwCallback::PhraseRecognitionEvent& halEvent,
- CallbackCookie cookie)
-{
- sp<SoundModel> model;
- {
- AutoMutex lock(mLock);
- model = mSoundModels.valueFor((SoundModelHandle)cookie);
- if (model == 0) {
- return Return<void>();
- }
- }
-
- struct sound_trigger_phrase_recognition_event *event =
- convertPhraseRecognitionEventFromHal(&halEvent);
- if (event == NULL) {
- return Return<void>();
- }
- event->common.model = model->mHandle;
- model->mRecognitionCallback(&event->common, model->mRecognitionCookie);
-
- free(event);
-
- return Return<void>();
-}
-
-::android::hardware::Return<void> SoundTriggerHalHidl::soundModelCallback(
- const V2_0_ISoundTriggerHwCallback::ModelEvent& halEvent,
- CallbackCookie cookie)
-{
- sp<SoundModel> model;
- {
- AutoMutex lock(mLock);
- model = mSoundModels.valueFor((SoundModelHandle)cookie);
- if (model == 0) {
- return Return<void>();
- }
- }
-
- struct sound_trigger_model_event *event = convertSoundModelEventFromHal(&halEvent);
- if (event == NULL) {
- return Return<void>();
- }
-
- event->model = model->mHandle;
- model->mSoundModelCallback(event, model->mSoundModelCookie);
-
- free(event);
-
- return Return<void>();
-}
-
-::android::hardware::Return<void> SoundTriggerHalHidl::recognitionCallback_2_1(
- const ISoundTriggerHwCallback::RecognitionEvent& event, CallbackCookie cookie) {
- // The data vector in the 'header' part of V2.1 structure is empty, thus copying is cheap.
- V2_0_ISoundTriggerHwCallback::RecognitionEvent event_2_0 = event.header;
- auto result = memoryAsVector(event.data, &event_2_0.data);
- return result.first ? recognitionCallback(event_2_0, cookie) : Void();
-}
-
-::android::hardware::Return<void> SoundTriggerHalHidl::phraseRecognitionCallback_2_1(
- const ISoundTriggerHwCallback::PhraseRecognitionEvent& event, int32_t cookie) {
- V2_0_ISoundTriggerHwCallback::PhraseRecognitionEvent event_2_0;
- // The data vector in the 'header' part of V2.1 structure is empty, thus copying is cheap.
- event_2_0.common = event.common.header;
- event_2_0.phraseExtras.setToExternal(
- const_cast<PhraseRecognitionExtra*>(event.phraseExtras.data()),
- event.phraseExtras.size());
- auto result = memoryAsVector(event.common.data, &event_2_0.common.data);
- return result.first ? phraseRecognitionCallback(event_2_0, cookie) : Void();
-}
-
-::android::hardware::Return<void> SoundTriggerHalHidl::soundModelCallback_2_1(
- const ISoundTriggerHwCallback::ModelEvent& event, CallbackCookie cookie) {
- // The data vector in the 'header' part of V2.1 structure is empty, thus copying is cheap.
- V2_0_ISoundTriggerHwCallback::ModelEvent event_2_0 = event.header;
- auto result = memoryAsVector(event.data, &event_2_0.data);
- return result.first ? soundModelCallback(event_2_0, cookie) : Void();
-}
-
-
-struct sound_trigger_model_event *SoundTriggerHalHidl::convertSoundModelEventFromHal(
- const V2_0_ISoundTriggerHwCallback::ModelEvent *halEvent)
-{
- struct sound_trigger_model_event *event = (struct sound_trigger_model_event *)malloc(
- sizeof(struct sound_trigger_model_event) +
- halEvent->data.size());
- if (event == NULL) {
- return NULL;
- }
-
- event->status = (int)halEvent->status;
- // event->model to be set by caller
- event->data_offset = sizeof(struct sound_trigger_model_event);
- event->data_size = halEvent->data.size();
- uint8_t *dst = (uint8_t *)event + event->data_offset;
- uint8_t *src = (uint8_t *)&halEvent->data[0];
- memcpy(dst, src, halEvent->data.size());
-
- return event;
-}
-
-void SoundTriggerHalHidl::convertPhraseRecognitionExtraFromHal(
- struct sound_trigger_phrase_recognition_extra *extra,
- const PhraseRecognitionExtra *halExtra)
-{
- extra->id = halExtra->id;
- extra->recognition_modes = halExtra->recognitionModes;
- extra->confidence_level = halExtra->confidenceLevel;
-
- size_t i;
- for (i = 0; i < halExtra->levels.size() && i < SOUND_TRIGGER_MAX_USERS; i++) {
- extra->levels[i].user_id = halExtra->levels[i].userId;
- extra->levels[i].level = halExtra->levels[i].levelPercent;
- }
- extra->num_levels = (unsigned int)i;
-}
-
-
-struct sound_trigger_phrase_recognition_event* SoundTriggerHalHidl::convertPhraseRecognitionEventFromHal(
- const V2_0_ISoundTriggerHwCallback::PhraseRecognitionEvent *halPhraseEvent)
-{
- if (halPhraseEvent->common.type != SoundModelType::KEYPHRASE) {
- ALOGE("Received non-keyphrase event type as PhraseRecognitionEvent");
- return NULL;
- }
- struct sound_trigger_phrase_recognition_event *phraseEvent =
- (struct sound_trigger_phrase_recognition_event *)malloc(
- sizeof(struct sound_trigger_phrase_recognition_event) +
- halPhraseEvent->common.data.size());
- if (phraseEvent == NULL) {
- return NULL;
- }
- phraseEvent->common.data_offset = sizeof(sound_trigger_phrase_recognition_event);
-
- for (unsigned int i = 0; i < halPhraseEvent->phraseExtras.size(); i++) {
- convertPhraseRecognitionExtraFromHal(&phraseEvent->phrase_extras[i],
- &halPhraseEvent->phraseExtras[i]);
- }
- phraseEvent->num_phrases = halPhraseEvent->phraseExtras.size();
-
- fillRecognitionEventFromHal(&phraseEvent->common, &halPhraseEvent->common);
- return phraseEvent;
-}
-
-struct sound_trigger_recognition_event *SoundTriggerHalHidl::convertRecognitionEventFromHal(
- const V2_0_ISoundTriggerHwCallback::RecognitionEvent *halEvent)
-{
- if (halEvent->type == SoundModelType::KEYPHRASE) {
- ALOGE("Received keyphrase event type as RecognitionEvent");
- return NULL;
- }
- struct sound_trigger_recognition_event *event;
- event = (struct sound_trigger_recognition_event *)malloc(
- sizeof(struct sound_trigger_recognition_event) + halEvent->data.size());
- if (event == NULL) {
- return NULL;
- }
- event->data_offset = sizeof(sound_trigger_recognition_event);
-
- fillRecognitionEventFromHal(event, halEvent);
- return event;
-}
-
-void SoundTriggerHalHidl::fillRecognitionEventFromHal(
- struct sound_trigger_recognition_event *event,
- const V2_0_ISoundTriggerHwCallback::RecognitionEvent *halEvent)
-{
- event->status = (int)halEvent->status;
- event->type = (sound_trigger_sound_model_type_t)halEvent->type;
- // event->model to be set by caller
- event->capture_available = (bool)halEvent->captureAvailable;
- event->capture_session = halEvent->captureSession;
- event->capture_delay_ms = halEvent->captureDelayMs;
- event->capture_preamble_ms = halEvent->capturePreambleMs;
- event->trigger_in_data = (bool)halEvent->triggerInData;
- event->audio_config.sample_rate = halEvent->audioConfig.sampleRateHz;
- event->audio_config.channel_mask = (audio_channel_mask_t)halEvent->audioConfig.channelMask;
- event->audio_config.format = (audio_format_t)halEvent->audioConfig.format;
-
- event->data_size = halEvent->data.size();
- uint8_t *dst = (uint8_t *)event + event->data_offset;
- uint8_t *src = (uint8_t *)&halEvent->data[0];
- memcpy(dst, src, halEvent->data.size());
-}
-
-} // namespace android
diff --git a/services/soundtrigger/SoundTriggerHalHidl.h b/services/soundtrigger/SoundTriggerHalHidl.h
deleted file mode 100644
index 25878d0..0000000
--- a/services/soundtrigger/SoundTriggerHalHidl.h
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_SOUNDTRIGGER_HAL_HIDL_H
-#define ANDROID_HARDWARE_SOUNDTRIGGER_HAL_HIDL_H
-
-#include <utility>
-
-#include <stdatomic.h>
-#include <utils/RefBase.h>
-#include <utils/KeyedVector.h>
-#include <utils/Vector.h>
-#include <utils/threads.h>
-#include "SoundTriggerHalInterface.h"
-#include <android/hardware/soundtrigger/2.0/types.h>
-#include <android/hardware/soundtrigger/2.3/types.h>
-#include <android/hardware/soundtrigger/2.1/ISoundTriggerHw.h>
-#include <android/hardware/soundtrigger/2.2/ISoundTriggerHw.h>
-#include <android/hardware/soundtrigger/2.3/ISoundTriggerHw.h>
-#include <android/hardware/soundtrigger/2.0/ISoundTriggerHwCallback.h>
-#include <android/hardware/soundtrigger/2.1/ISoundTriggerHwCallback.h>
-
-namespace android {
-
-using ::android::hardware::audio::common::V2_0::Uuid;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::soundtrigger::V2_0::ConfidenceLevel;
-using ::android::hardware::soundtrigger::V2_0::PhraseRecognitionExtra;
-using ::android::hardware::soundtrigger::V2_0::SoundModelType;
-using ::android::hardware::soundtrigger::V2_0::SoundModelHandle;
-using ::android::hardware::soundtrigger::V2_0::ISoundTriggerHw;
-using V2_0_ISoundTriggerHwCallback =
- ::android::hardware::soundtrigger::V2_0::ISoundTriggerHwCallback;
-using V2_1_ISoundTriggerHw =
- ::android::hardware::soundtrigger::V2_1::ISoundTriggerHw;
-using V2_1_ISoundTriggerHwCallback =
- ::android::hardware::soundtrigger::V2_1::ISoundTriggerHwCallback;
-using ::android::hidl::memory::V1_0::IMemory;
-using V2_2_ISoundTriggerHw =
- ::android::hardware::soundtrigger::V2_2::ISoundTriggerHw;
-using V2_3_ISoundTriggerHw =
- ::android::hardware::soundtrigger::V2_3::ISoundTriggerHw;
-using V2_3_ModelParameter =
- ::android::hardware::soundtrigger::V2_3::ModelParameter;
-using V2_3_OptionalModelParameterRange =
- ::android::hardware::soundtrigger::V2_3::OptionalModelParameterRange;
-
-class SoundTriggerHalHidl : public SoundTriggerHalInterface,
- public virtual V2_1_ISoundTriggerHwCallback
-
-{
-public:
- virtual int getProperties(struct sound_trigger_properties *properties);
-
- /*
- * Load a sound model. Once loaded, recognition of this model can be started and stopped.
- * Only one active recognition per model at a time. The SoundTrigger service will handle
- * concurrent recognition requests by different users/applications on the same model.
- * The implementation returns a unique handle used by other functions (unload_sound_model(),
- * start_recognition(), etc...
- */
- virtual int loadSoundModel(struct sound_trigger_sound_model *sound_model,
- sound_model_callback_t callback,
- void *cookie,
- sound_model_handle_t *handle);
-
- /*
- * Unload a sound model. A sound model can be unloaded to make room for a new one to overcome
- * implementation limitations.
- */
- virtual int unloadSoundModel(sound_model_handle_t handle);
-
- /* Start recognition on a given model. Only one recognition active at a time per model.
- * Once recognition succeeds of fails, the callback is called.
- * TODO: group recognition configuration parameters into one struct and add key phrase options.
- */
- virtual int startRecognition(sound_model_handle_t handle,
- const struct sound_trigger_recognition_config *config,
- recognition_callback_t callback,
- void *cookie);
-
- /* Stop recognition on a given model.
- * The implementation does not have to call the callback when stopped via this method.
- */
- virtual int stopRecognition(sound_model_handle_t handle);
-
- /* Stop recognition on all models.
- * Only supported for device api versions SOUND_TRIGGER_DEVICE_API_VERSION_1_1 or above.
- * If no implementation is provided, stop_recognition will be called for each running model.
- */
- virtual int stopAllRecognitions();
-
- /* Get the current state of a given model.
- * Returns 0 or an error code. If successful the state will be returned asynchronously
- * via a recognition event in the callback method that was registered in the
- * startRecognition() method.
- * Only supported for device api versions SOUND_TRIGGER_DEVICE_API_VERSION_1_2 or above.
- */
- virtual int getModelState(sound_model_handle_t handle);
-
- /* Set a model specific ModelParameter with the given value. This parameter
- * will keep its value for the duration the model is loaded regardless of starting and
- * stopping recognition. Once the model is unloaded, the value will be lost.
- * Returns 0 or an error code.
- * Only supported for device api versions SOUND_TRIGGER_DEVICE_API_VERSION_1_3 or above.
- */
- int setParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t model_param, int32_t value);
-
- /* Get a model specific ModelParameter. This parameter will keep its value
- * for the duration the model is loaded regardless of starting and stopping recognition.
- * Once the model is unloaded, the value will be lost. If the value is not set, a default
- * value is returned. See sound_trigger_model_parameter_t for parameter default values.
- * Returns 0 or an error code. On return 0, value pointer will be set.
- * Only supported for device api versions SOUND_TRIGGER_DEVICE_API_VERSION_1_3 or above.
- */
- int getParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t model_param, int32_t* value);
-
- /* Get supported parameter attributes with respect to the provided model
- * handle. Along with determining the valid range, this API is also used
- * to determine if a given parameter ID is supported at all by the
- * modelHandle for use with getParameter and setParameter APIs.
- */
- int queryParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t model_param,
- sound_trigger_model_parameter_range_t* param_range);
-
- // ISoundTriggerHwCallback
- virtual ::android::hardware::Return<void> recognitionCallback(
- const V2_0_ISoundTriggerHwCallback::RecognitionEvent& event, CallbackCookie cookie);
- virtual ::android::hardware::Return<void> phraseRecognitionCallback(
- const V2_0_ISoundTriggerHwCallback::PhraseRecognitionEvent& event, int32_t cookie);
- virtual ::android::hardware::Return<void> soundModelCallback(
- const V2_0_ISoundTriggerHwCallback::ModelEvent& event, CallbackCookie cookie);
- virtual ::android::hardware::Return<void> recognitionCallback_2_1(
- const RecognitionEvent& event, CallbackCookie cookie);
- virtual ::android::hardware::Return<void> phraseRecognitionCallback_2_1(
- const PhraseRecognitionEvent& event, int32_t cookie);
- virtual ::android::hardware::Return<void> soundModelCallback_2_1(
- const ModelEvent& event, CallbackCookie cookie);
-private:
- class SoundModel : public RefBase {
- public:
- SoundModel(sound_model_handle_t handle, sound_model_callback_t callback,
- void *cookie, android::hardware::soundtrigger::V2_0::SoundModelHandle halHandle)
- : mHandle(handle), mHalHandle(halHandle),
- mSoundModelCallback(callback), mSoundModelCookie(cookie),
- mRecognitionCallback(NULL), mRecognitionCookie(NULL) {}
- ~SoundModel() {}
-
- sound_model_handle_t mHandle;
- android::hardware::soundtrigger::V2_0::SoundModelHandle mHalHandle;
- sound_model_callback_t mSoundModelCallback;
- void * mSoundModelCookie;
- recognition_callback_t mRecognitionCallback;
- void * mRecognitionCookie;
- };
-
- friend class SoundTriggerHalInterface;
-
- explicit SoundTriggerHalHidl(const char *moduleName = NULL);
- virtual ~SoundTriggerHalHidl();
-
- void convertUuidToHal(Uuid *halUuid,
- const sound_trigger_uuid_t *uuid);
- void convertUuidFromHal(sound_trigger_uuid_t *uuid,
- const Uuid *halUuid);
-
- void convertPropertiesFromHal(
- struct sound_trigger_properties *properties,
- const ISoundTriggerHw::Properties *halProperties);
- static void convertModelParameterToHal(V2_3_ModelParameter* halParam,
- sound_trigger_model_parameter_t param);
-
- void convertTriggerPhraseToHal(
- ISoundTriggerHw::Phrase *halTriggerPhrase,
- const struct sound_trigger_phrase *triggerPhrase);
- void convertTriggerPhrasesToHal(
- hidl_vec<ISoundTriggerHw::Phrase> *halTriggerPhrases,
- struct sound_trigger_phrase_sound_model *keyPhraseModel);
- void convertSoundModelToHal(ISoundTriggerHw::SoundModel *halModel,
- const struct sound_trigger_sound_model *soundModel);
- std::pair<bool, sp<IMemory>> convertSoundModelToHal(
- V2_1_ISoundTriggerHw::SoundModel *halModel,
- const struct sound_trigger_sound_model *soundModel)
- __attribute__((warn_unused_result));
- void convertPhraseSoundModelToHal(ISoundTriggerHw::PhraseSoundModel *halKeyPhraseModel,
- const struct sound_trigger_sound_model *soundModel);
- std::pair<bool, sp<IMemory>> convertPhraseSoundModelToHal(
- V2_1_ISoundTriggerHw::PhraseSoundModel *halKeyPhraseModel,
- const struct sound_trigger_sound_model *soundModel)
- __attribute__((warn_unused_result));
-
- void convertPhraseRecognitionExtraToHal(
- PhraseRecognitionExtra *halExtra,
- const struct sound_trigger_phrase_recognition_extra *extra);
- void convertRecognitionConfigToHal(ISoundTriggerHw::RecognitionConfig *halConfig,
- const struct sound_trigger_recognition_config *config);
- std::pair<bool, sp<IMemory>> convertRecognitionConfigToHal(
- V2_1_ISoundTriggerHw::RecognitionConfig *halConfig,
- const struct sound_trigger_recognition_config *config)
- __attribute__((warn_unused_result));
-
- struct sound_trigger_model_event *convertSoundModelEventFromHal(
- const V2_0_ISoundTriggerHwCallback::ModelEvent *halEvent);
- void convertPhraseRecognitionExtraFromHal(
- struct sound_trigger_phrase_recognition_extra *extra,
- const PhraseRecognitionExtra *halExtra);
- struct sound_trigger_phrase_recognition_event* convertPhraseRecognitionEventFromHal(
- const V2_0_ISoundTriggerHwCallback::PhraseRecognitionEvent *halPhraseEvent);
- struct sound_trigger_recognition_event *convertRecognitionEventFromHal(
- const V2_0_ISoundTriggerHwCallback::RecognitionEvent *halEvent);
- void fillRecognitionEventFromHal(
- struct sound_trigger_recognition_event *event,
- const V2_0_ISoundTriggerHwCallback::RecognitionEvent *halEvent);
-
- uint32_t nextUniqueId();
- sp<ISoundTriggerHw> getService();
- sp<V2_1_ISoundTriggerHw> toService2_1(const sp<ISoundTriggerHw>& s);
- sp<V2_2_ISoundTriggerHw> toService2_2(const sp<ISoundTriggerHw>& s);
- sp<V2_3_ISoundTriggerHw> toService2_3(const sp<ISoundTriggerHw>& s);
- sp<SoundModel> getModel(sound_model_handle_t handle);
- sp<SoundModel> removeModel(sound_model_handle_t handle);
-
- static pthread_once_t sOnceControl;
- static void sOnceInit();
-
- Mutex mLock;
- Mutex mHalLock;
- const char *mModuleName;
- volatile atomic_uint_fast32_t mNextUniqueId;
- // Effect chains without a valid thread
- DefaultKeyedVector< sound_model_handle_t , sp<SoundModel> > mSoundModels;
- sp<::android::hardware::soundtrigger::V2_0::ISoundTriggerHw> mISoundTrigger;
-};
-
-} // namespace android
-
-#endif // ANDROID_HARDWARE_SOUNDTRIGGER_HAL_HIDL_H
diff --git a/services/soundtrigger/SoundTriggerHalInterface.h b/services/soundtrigger/SoundTriggerHalInterface.h
deleted file mode 100644
index e1fffff..0000000
--- a/services/soundtrigger/SoundTriggerHalInterface.h
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_SOUNDTRIGGER_HAL_INTERFACE_H
-#define ANDROID_HARDWARE_SOUNDTRIGGER_HAL_INTERFACE_H
-
-#include <utils/RefBase.h>
-#include <system/sound_trigger.h>
-#include <hardware/sound_trigger.h>
-
-namespace android {
-
-class SoundTriggerHalInterface : public virtual RefBase
-{
-public:
- /* get a sound trigger HAL instance */
- static sp<SoundTriggerHalInterface> connectModule(const char *moduleName);
-
- virtual ~SoundTriggerHalInterface() {}
-
- virtual int getProperties(struct sound_trigger_properties *properties) = 0;
-
- /*
- * Load a sound model. Once loaded, recognition of this model can be started and stopped.
- * Only one active recognition per model at a time. The SoundTrigger service will handle
- * concurrent recognition requests by different users/applications on the same model.
- * The implementation returns a unique handle used by other functions (unload_sound_model(),
- * start_recognition(), etc...
- */
- virtual int loadSoundModel(struct sound_trigger_sound_model *sound_model,
- sound_model_callback_t callback,
- void *cookie,
- sound_model_handle_t *handle) = 0;
-
- /*
- * Unload a sound model. A sound model can be unloaded to make room for a new one to overcome
- * implementation limitations.
- */
- virtual int unloadSoundModel(sound_model_handle_t handle) = 0;
-
- /* Start recognition on a given model. Only one recognition active at a time per model.
- * Once recognition succeeds of fails, the callback is called.
- * TODO: group recognition configuration parameters into one struct and add key phrase options.
- */
- virtual int startRecognition(sound_model_handle_t handle,
- const struct sound_trigger_recognition_config *config,
- recognition_callback_t callback,
- void *cookie) = 0;
-
- /* Stop recognition on a given model.
- * The implementation does not have to call the callback when stopped via this method.
- */
- virtual int stopRecognition(sound_model_handle_t handle) = 0;
-
- /* Stop recognition on all models.
- * Only supported for device api versions SOUND_TRIGGER_DEVICE_API_VERSION_1_1 or above.
- * If no implementation is provided, stop_recognition will be called for each running model.
- */
- virtual int stopAllRecognitions() = 0;
-
- /* Get the current state of a given model.
- * Returns 0 or an error code. If successful the state will be returned asynchronously
- * via a recognition event in the callback method that was registered in the
- * startRecognition() method.
- * Only supported for device api versions SOUND_TRIGGER_DEVICE_API_VERSION_1_2 or above.
- */
- virtual int getModelState(sound_model_handle_t handle) = 0;
-
- /* Set a model specific ModelParameter with the given value. This parameter
- * will keep its value for the duration the model is loaded regardless of starting and stopping
- * recognition. Once the model is unloaded, the value will be lost.
- * Returns 0 or an error code.
- * Only supported for device api versions SOUND_TRIGGER_DEVICE_API_VERSION_1_3 or above.
- */
- virtual int setParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t model_param, int32_t value) = 0;
-
- /* Get a model specific ModelParameter. This parameter will keep its value
- * for the duration the model is loaded regardless of starting and stopping recognition.
- * Once the model is unloaded, the value will be lost. If the value is not set, a default
- * value is returned. See sound_trigger_model_parameter_t for parameter default values.
- * Returns 0 or an error code. On return 0, value pointer will be set.
- * Only supported for device api versions SOUND_TRIGGER_DEVICE_API_VERSION_1_3 or above.
- */
- virtual int getParameter(sound_model_handle_t sound_model_handle,
- sound_trigger_model_parameter_t model_param, int32_t* value) = 0;
-
- /* Get supported parameter attributes with respect to the provided model
- * handle. Along with determining the valid range, this API is also used
- * to determine if a given parameter ID is supported at all by the
- * modelHandle for use with getParameter and setParameter APIs.
- */
- virtual int queryParameter(sound_model_handle_t sound_model_handle,
- sound_trigger_model_parameter_t model_param,
- sound_trigger_model_parameter_range_t* param_range) = 0;
-
-protected:
- SoundTriggerHalInterface() {}
-};
-
-} // namespace android
-
-#endif // ANDROID_HARDWARE_SOUNDTRIGGER_HAL_INTERFACE_H
diff --git a/services/soundtrigger/SoundTriggerHwService.cpp b/services/soundtrigger/SoundTriggerHwService.cpp
deleted file mode 100644
index 4d8b0da..0000000
--- a/services/soundtrigger/SoundTriggerHwService.cpp
+++ /dev/null
@@ -1,1249 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "SoundTriggerHwService"
-//#define LOG_NDEBUG 0
-
-#include <stdio.h>
-#include <string.h>
-#include <sys/types.h>
-#include <pthread.h>
-
-#include <audio_utils/clock.h>
-#include <system/sound_trigger.h>
-#include <cutils/atomic.h>
-#include <cutils/properties.h>
-#include <hardware/hardware.h>
-#include <media/AudioSystem.h>
-#include <mediautils/ServiceUtilities.h>
-#include <utils/Errors.h>
-#include <utils/Log.h>
-#include <binder/IServiceManager.h>
-#include <binder/MemoryBase.h>
-#include <binder/MemoryHeapBase.h>
-#include <system/sound_trigger.h>
-#include "SoundTriggerHwService.h"
-
-#define HW_MODULE_PREFIX "primary"
-namespace android {
-
-namespace {
-
-// Given an IMemory, returns a copy of its content along with its size.
-// Returns nullptr on failure or if input is nullptr.
-std::pair<std::unique_ptr<uint8_t[]>,
- size_t> CopyToArray(const sp<IMemory>& mem) {
- if (mem == nullptr) {
- return std::make_pair(nullptr, 0);
- }
-
- const size_t size = mem->size();
- if (size == 0) {
- return std::make_pair(nullptr, 0);
- }
-
- std::unique_ptr<uint8_t[]> ar = std::make_unique<uint8_t[]>(size);
- if (ar == nullptr) {
- return std::make_pair(nullptr, 0);
- }
-
- memcpy(ar.get(), mem->unsecurePointer(), size);
- return std::make_pair(std::move(ar), size);
-}
-
-}
-
-SoundTriggerHwService::SoundTriggerHwService()
- : BnSoundTriggerHwService(),
- mNextUniqueId(1),
- mMemoryDealer(new MemoryDealer(1024 * 1024, "SoundTriggerHwService")),
- mCaptureState(false)
-{
-}
-
-void SoundTriggerHwService::onFirstRef()
-{
- int rc;
-
- sp<SoundTriggerHalInterface> halInterface =
- SoundTriggerHalInterface::connectModule(HW_MODULE_PREFIX);
-
- if (halInterface == 0) {
- ALOGW("could not connect to HAL");
- return;
- }
- sound_trigger_module_descriptor descriptor;
- rc = halInterface->getProperties(&descriptor.properties);
- if (rc != 0) {
- ALOGE("could not read implementation properties");
- return;
- }
- descriptor.handle =
- (sound_trigger_module_handle_t)android_atomic_inc(&mNextUniqueId);
- ALOGI("loaded default module %s, handle %d", descriptor.properties.description,
- descriptor.handle);
-
- sp<Module> module = new Module(this, halInterface, descriptor);
- mModules.add(descriptor.handle, module);
- mCallbackThread = new CallbackThread(this);
-}
-
-SoundTriggerHwService::~SoundTriggerHwService()
-{
- if (mCallbackThread != 0) {
- mCallbackThread->exit();
- }
-}
-
-status_t SoundTriggerHwService::listModules(const String16& opPackageName,
- struct sound_trigger_module_descriptor *modules,
- uint32_t *numModules)
-{
- ALOGV("listModules");
- if (!captureHotwordAllowed(opPackageName,
- IPCThreadState::self()->getCallingPid(),
- IPCThreadState::self()->getCallingUid())) {
- return PERMISSION_DENIED;
- }
-
- AutoMutex lock(mServiceLock);
- if (numModules == NULL || (*numModules != 0 && modules == NULL)) {
- return BAD_VALUE;
- }
- size_t maxModules = *numModules;
- *numModules = mModules.size();
- for (size_t i = 0; i < mModules.size() && i < maxModules; i++) {
- modules[i] = mModules.valueAt(i)->descriptor();
- }
- return NO_ERROR;
-}
-
-status_t SoundTriggerHwService::attach(const String16& opPackageName,
- const sound_trigger_module_handle_t handle,
- const sp<ISoundTriggerClient>& client,
- sp<ISoundTrigger>& moduleInterface)
-{
- ALOGV("attach module %d", handle);
- if (!captureHotwordAllowed(opPackageName,
- IPCThreadState::self()->getCallingPid(),
- IPCThreadState::self()->getCallingUid())) {
- return PERMISSION_DENIED;
- }
-
- AutoMutex lock(mServiceLock);
- moduleInterface.clear();
- if (client == 0) {
- return BAD_VALUE;
- }
- ssize_t index = mModules.indexOfKey(handle);
- if (index < 0) {
- return BAD_VALUE;
- }
- sp<Module> module = mModules.valueAt(index);
-
- sp<ModuleClient> moduleClient = module->addClient(client, opPackageName);
- if (moduleClient == 0) {
- return NO_INIT;
- }
-
- moduleClient->setCaptureState_l(mCaptureState);
- moduleInterface = moduleClient;
-
- return NO_ERROR;
-}
-
-status_t SoundTriggerHwService::setCaptureState(bool active)
-{
- ALOGV("setCaptureState %d", active);
- AutoMutex lock(mServiceLock);
- mCaptureState = active;
- for (size_t i = 0; i < mModules.size(); i++) {
- mModules.valueAt(i)->setCaptureState_l(active);
- }
- return NO_ERROR;
-}
-
-
-static const int kDumpLockTimeoutNs = 1 * NANOS_PER_SECOND;
-
-static bool dumpTryLock(Mutex& mutex)
-{
- status_t err = mutex.timedLock(kDumpLockTimeoutNs);
- return err == NO_ERROR;
-}
-
-status_t SoundTriggerHwService::dump(int fd, const Vector<String16>& args __unused) {
- String8 result;
- if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
- result.appendFormat("Permission Denial: can't dump SoundTriggerHwService");
- write(fd, result.string(), result.size());
- } else {
- bool locked = dumpTryLock(mServiceLock);
- // failed to lock - SoundTriggerHwService is probably deadlocked
- if (!locked) {
- result.append("SoundTriggerHwService may be deadlocked\n");
- write(fd, result.string(), result.size());
- }
-
- if (locked) mServiceLock.unlock();
- }
- return NO_ERROR;
-}
-
-status_t SoundTriggerHwService::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
- return BnSoundTriggerHwService::onTransact(code, data, reply, flags);
-}
-
-
-// static
-void SoundTriggerHwService::recognitionCallback(struct sound_trigger_recognition_event *event,
- void *cookie)
-{
- Module *module = (Module *)cookie;
- if (module == NULL) {
- return;
- }
- sp<SoundTriggerHwService> service = module->service().promote();
- if (service == 0) {
- return;
- }
-
- service->sendRecognitionEvent(event, module);
-}
-
-sp<IMemory> SoundTriggerHwService::prepareRecognitionEvent(
- struct sound_trigger_recognition_event *event)
-{
- AutoMutex lock(mMemoryDealerLock);
- sp<IMemory> eventMemory;
-
- //sanitize event
- switch (event->type) {
- case SOUND_MODEL_TYPE_KEYPHRASE:
- ALOGW_IF(event->data_size != 0 && event->data_offset !=
- sizeof(struct sound_trigger_phrase_recognition_event),
- "prepareRecognitionEvent(): invalid data offset %u for keyphrase event type",
- event->data_offset);
- event->data_offset = sizeof(struct sound_trigger_phrase_recognition_event);
- break;
- case SOUND_MODEL_TYPE_GENERIC:
- ALOGW_IF(event->data_size != 0 && event->data_offset !=
- sizeof(struct sound_trigger_generic_recognition_event),
- "prepareRecognitionEvent(): invalid data offset %u for generic event type",
- event->data_offset);
- event->data_offset = sizeof(struct sound_trigger_generic_recognition_event);
- break;
- case SOUND_MODEL_TYPE_UNKNOWN:
- ALOGW_IF(event->data_size != 0 && event->data_offset !=
- sizeof(struct sound_trigger_recognition_event),
- "prepareRecognitionEvent(): invalid data offset %u for unknown event type",
- event->data_offset);
- event->data_offset = sizeof(struct sound_trigger_recognition_event);
- break;
- default:
- return eventMemory;
- }
-
- size_t size = event->data_offset + event->data_size;
- eventMemory = mMemoryDealer->allocate(size);
- if (eventMemory == 0 || eventMemory->unsecurePointer() == NULL) {
- eventMemory.clear();
- return eventMemory;
- }
- memcpy(eventMemory->unsecurePointer(), event, size);
-
- return eventMemory;
-}
-
-void SoundTriggerHwService::sendRecognitionEvent(struct sound_trigger_recognition_event *event,
- Module *module)
-{
- if (module == NULL) {
- return;
- }
- sp<IMemory> eventMemory = prepareRecognitionEvent(event);
- if (eventMemory == 0) {
- return;
- }
-
- sp<CallbackEvent> callbackEvent = new CallbackEvent(CallbackEvent::TYPE_RECOGNITION,
- eventMemory);
- callbackEvent->setModule(module);
- sendCallbackEvent(callbackEvent);
-}
-
-// static
-void SoundTriggerHwService::soundModelCallback(struct sound_trigger_model_event *event,
- void *cookie)
-{
- Module *module = (Module *)cookie;
- if (module == NULL) {
- return;
- }
- sp<SoundTriggerHwService> service = module->service().promote();
- if (service == 0) {
- return;
- }
-
- service->sendSoundModelEvent(event, module);
-}
-
-sp<IMemory> SoundTriggerHwService::prepareSoundModelEvent(struct sound_trigger_model_event *event)
-{
- AutoMutex lock(mMemoryDealerLock);
- sp<IMemory> eventMemory;
-
- size_t size = event->data_offset + event->data_size;
- eventMemory = mMemoryDealer->allocate(size);
- if (eventMemory == 0 || eventMemory->unsecurePointer() == NULL) {
- eventMemory.clear();
- return eventMemory;
- }
- memcpy(eventMemory->unsecurePointer(), event, size);
-
- return eventMemory;
-}
-
-void SoundTriggerHwService::sendSoundModelEvent(struct sound_trigger_model_event *event,
- Module *module)
-{
- sp<IMemory> eventMemory = prepareSoundModelEvent(event);
- if (eventMemory == 0) {
- return;
- }
- sp<CallbackEvent> callbackEvent = new CallbackEvent(CallbackEvent::TYPE_SOUNDMODEL,
- eventMemory);
- callbackEvent->setModule(module);
- sendCallbackEvent(callbackEvent);
-}
-
-
-sp<IMemory> SoundTriggerHwService::prepareServiceStateEvent(sound_trigger_service_state_t state)
-{
- AutoMutex lock(mMemoryDealerLock);
- sp<IMemory> eventMemory;
-
- size_t size = sizeof(sound_trigger_service_state_t);
- eventMemory = mMemoryDealer->allocate(size);
- if (eventMemory == 0 || eventMemory->unsecurePointer() == NULL) {
- eventMemory.clear();
- return eventMemory;
- }
- *((sound_trigger_service_state_t *)eventMemory->unsecurePointer()) = state;
- return eventMemory;
-}
-
-void SoundTriggerHwService::sendServiceStateEvent(sound_trigger_service_state_t state,
- Module *module)
-{
- sp<IMemory> eventMemory = prepareServiceStateEvent(state);
- if (eventMemory == 0) {
- return;
- }
- sp<CallbackEvent> callbackEvent = new CallbackEvent(CallbackEvent::TYPE_SERVICE_STATE,
- eventMemory);
- callbackEvent->setModule(module);
- sendCallbackEvent(callbackEvent);
-}
-
-void SoundTriggerHwService::sendServiceStateEvent(sound_trigger_service_state_t state,
- ModuleClient *moduleClient)
-{
- sp<IMemory> eventMemory = prepareServiceStateEvent(state);
- if (eventMemory == 0) {
- return;
- }
- sp<CallbackEvent> callbackEvent = new CallbackEvent(CallbackEvent::TYPE_SERVICE_STATE,
- eventMemory);
- callbackEvent->setModuleClient(moduleClient);
- sendCallbackEvent(callbackEvent);
-}
-
-void SoundTriggerHwService::sendCallbackEvent(const sp<CallbackEvent>& event)
-{
- mCallbackThread->sendCallbackEvent(event);
-}
-
-void SoundTriggerHwService::onCallbackEvent(const sp<CallbackEvent>& event)
-{
- ALOGV("onCallbackEvent");
- sp<Module> module;
- sp<ModuleClient> moduleClient;
- {
- AutoMutex lock(mServiceLock);
- //CallbackEvent is either for Module or ModuleClient
- module = event->mModule.promote();
- if (module == 0) {
- moduleClient = event->mModuleClient.promote();
- if (moduleClient == 0) {
- return;
- }
- } else {
- // Sanity check on this being a Module we know about.
- bool foundModule = false;
- for (size_t i = 0; i < mModules.size(); i++) {
- if (mModules.valueAt(i).get() == module.get()) {
- foundModule = true;
- break;
- }
- }
- if (!foundModule) {
- ALOGE("onCallbackEvent for unknown module");
- return;
- }
- }
- }
- if (module != 0) {
- ALOGV("onCallbackEvent for module");
- module->onCallbackEvent(event);
- } else if (moduleClient != 0) {
- ALOGV("onCallbackEvent for moduleClient");
- moduleClient->onCallbackEvent(event);
- }
- {
- AutoMutex lock(mServiceLock);
- // clear now to execute with mServiceLock locked
- event->mMemory.clear();
- }
-}
-
-#undef LOG_TAG
-#define LOG_TAG "SoundTriggerHwService::CallbackThread"
-
-SoundTriggerHwService::CallbackThread::CallbackThread(const wp<SoundTriggerHwService>& service)
- : mService(service)
-{
-}
-
-SoundTriggerHwService::CallbackThread::~CallbackThread()
-{
- while (!mEventQueue.isEmpty()) {
- mEventQueue[0]->mMemory.clear();
- mEventQueue.removeAt(0);
- }
-}
-
-void SoundTriggerHwService::CallbackThread::onFirstRef()
-{
- run("soundTrigger cbk", ANDROID_PRIORITY_URGENT_AUDIO);
-}
-
-bool SoundTriggerHwService::CallbackThread::threadLoop()
-{
- while (!exitPending()) {
- sp<CallbackEvent> event;
- sp<SoundTriggerHwService> service;
- {
- Mutex::Autolock _l(mCallbackLock);
- while (mEventQueue.isEmpty() && !exitPending()) {
- ALOGV("CallbackThread::threadLoop() sleep");
- mCallbackCond.wait(mCallbackLock);
- ALOGV("CallbackThread::threadLoop() wake up");
- }
- if (exitPending()) {
- break;
- }
- event = mEventQueue[0];
- mEventQueue.removeAt(0);
- service = mService.promote();
- }
- if (service != 0) {
- service->onCallbackEvent(event);
- }
- }
- return false;
-}
-
-void SoundTriggerHwService::CallbackThread::exit()
-{
- Mutex::Autolock _l(mCallbackLock);
- requestExit();
- mCallbackCond.broadcast();
-}
-
-void SoundTriggerHwService::CallbackThread::sendCallbackEvent(
- const sp<SoundTriggerHwService::CallbackEvent>& event)
-{
- AutoMutex lock(mCallbackLock);
- mEventQueue.add(event);
- mCallbackCond.signal();
-}
-
-SoundTriggerHwService::CallbackEvent::CallbackEvent(event_type type, sp<IMemory> memory)
- : mType(type), mMemory(memory)
-{
-}
-
-SoundTriggerHwService::CallbackEvent::~CallbackEvent()
-{
-}
-
-
-#undef LOG_TAG
-#define LOG_TAG "SoundTriggerHwService::Module"
-
-SoundTriggerHwService::Module::Module(const sp<SoundTriggerHwService>& service,
- const sp<SoundTriggerHalInterface>& halInterface,
- sound_trigger_module_descriptor descriptor)
- : mService(service), mHalInterface(halInterface), mDescriptor(descriptor),
- mServiceState(SOUND_TRIGGER_STATE_NO_INIT)
-{
-}
-
-SoundTriggerHwService::Module::~Module() {
- mModuleClients.clear();
-}
-
-sp<SoundTriggerHwService::ModuleClient>
-SoundTriggerHwService::Module::addClient(const sp<ISoundTriggerClient>& client,
- const String16& opPackageName)
-{
- AutoMutex lock(mLock);
- sp<ModuleClient> moduleClient;
-
- for (size_t i = 0; i < mModuleClients.size(); i++) {
- if (mModuleClients[i]->client() == client) {
- // Client already present, reuse client
- return moduleClient;
- }
- }
- moduleClient = new ModuleClient(this, client, opPackageName);
-
- ALOGV("addClient() client %p", moduleClient.get());
- mModuleClients.add(moduleClient);
-
- return moduleClient;
-}
-
-void SoundTriggerHwService::Module::detach(const sp<ModuleClient>& moduleClient)
-{
- ALOGV("Module::detach()");
- Vector<audio_session_t> releasedSessions;
-
- {
- AutoMutex lock(mLock);
- ssize_t index = -1;
-
- for (size_t i = 0; i < mModuleClients.size(); i++) {
- if (mModuleClients[i] == moduleClient) {
- index = i;
- break;
- }
- }
- if (index == -1) {
- return;
- }
-
- ALOGV("remove client %p", moduleClient.get());
- mModuleClients.removeAt(index);
-
- // Iterate in reverse order as models are removed from list inside the loop.
- for (size_t i = mModels.size(); i > 0; i--) {
- sp<Model> model = mModels.valueAt(i - 1);
- if (moduleClient == model->mModuleClient) {
- mModels.removeItemsAt(i - 1);
- ALOGV("detach() unloading model %d", model->mHandle);
- if (mHalInterface != 0) {
- if (model->mState == Model::STATE_ACTIVE) {
- mHalInterface->stopRecognition(model->mHandle);
- }
- mHalInterface->unloadSoundModel(model->mHandle);
- }
- releasedSessions.add(model->mCaptureSession);
- }
- }
- }
-
- for (size_t i = 0; i < releasedSessions.size(); i++) {
- // do not call AudioSystem methods with mLock held
- AudioSystem::releaseSoundTriggerSession(releasedSessions[i]);
- }
-}
-
-status_t SoundTriggerHwService::Module::loadSoundModel(const sp<IMemory>& modelMemory,
- sp<ModuleClient> moduleClient,
- sound_model_handle_t *handle)
-{
- ALOGV("loadSoundModel() handle");
- if (mHalInterface == 0) {
- return NO_INIT;
- }
-
- auto immutableMemory = CopyToArray(modelMemory);
- if (immutableMemory.first == nullptr) {
- return NO_MEMORY;
- }
-
- struct sound_trigger_sound_model* sound_model =
- (struct sound_trigger_sound_model*) immutableMemory.first.get();
-
- size_t structSize;
- if (sound_model->type == SOUND_MODEL_TYPE_KEYPHRASE) {
- structSize = sizeof(struct sound_trigger_phrase_sound_model);
- } else {
- structSize = sizeof(struct sound_trigger_sound_model);
- }
-
- if (sound_model->data_offset < structSize ||
- sound_model->data_size > (UINT_MAX - sound_model->data_offset) ||
- immutableMemory.second < sound_model->data_offset ||
- sound_model->data_size >
- (immutableMemory.second - sound_model->data_offset)) {
- android_errorWriteLog(0x534e4554, "30148546");
- ALOGE("loadSoundModel() data_size is too big");
- return BAD_VALUE;
- }
-
- audio_session_t session;
- audio_io_handle_t ioHandle;
- audio_devices_t device;
- // do not call AudioSystem methods with mLock held
- status_t status = AudioSystem::acquireSoundTriggerSession(&session, &ioHandle, &device);
- if (status != NO_ERROR) {
- return status;
- }
-
- {
- AutoMutex lock(mLock);
-
- if (mModels.size() >= mDescriptor.properties.max_sound_models) {
- ALOGW("loadSoundModel(): Not loading, max number of models (%d) would be exceeded",
- mDescriptor.properties.max_sound_models);
- status = INVALID_OPERATION;
- goto exit;
- }
-
- status = mHalInterface->loadSoundModel(sound_model,
- SoundTriggerHwService::soundModelCallback,
- this, handle);
- if (status != NO_ERROR) {
- goto exit;
- }
-
- sp<Model> model = new Model(*handle, session, ioHandle, device, sound_model->type,
- moduleClient);
- mModels.replaceValueFor(*handle, model);
- }
-exit:
- if (status != NO_ERROR) {
- // do not call AudioSystem methods with mLock held
- AudioSystem::releaseSoundTriggerSession(session);
- }
- return status;
-}
-
-status_t SoundTriggerHwService::Module::unloadSoundModel(sound_model_handle_t handle)
-{
- ALOGV("unloadSoundModel() model handle %d", handle);
- status_t status;
- audio_session_t session;
-
- {
- AutoMutex lock(mLock);
- if (mHalInterface == 0) {
- return NO_INIT;
- }
- ssize_t index = mModels.indexOfKey(handle);
- if (index < 0) {
- return BAD_VALUE;
- }
- sp<Model> model = mModels.valueAt(index);
- mModels.removeItem(handle);
- if (model->mState == Model::STATE_ACTIVE) {
- mHalInterface->stopRecognition(model->mHandle);
- model->mState = Model::STATE_IDLE;
- }
- status = mHalInterface->unloadSoundModel(handle);
- session = model->mCaptureSession;
- }
- // do not call AudioSystem methods with mLock held
- AudioSystem::releaseSoundTriggerSession(session);
- return status;
-}
-
-status_t SoundTriggerHwService::Module::startRecognition(sound_model_handle_t handle,
- const sp<IMemory>& dataMemory)
-{
- ALOGV("startRecognition() model handle %d", handle);
- if (mHalInterface == 0) {
- return NO_INIT;
- }
-
- auto immutableMemory = CopyToArray(dataMemory);
- if (immutableMemory.first == nullptr) {
- return NO_MEMORY;
- }
-
- struct sound_trigger_recognition_config* config =
- (struct sound_trigger_recognition_config*) immutableMemory.first.get();
-
- if (config->data_offset < sizeof(struct sound_trigger_recognition_config) ||
- config->data_size > (UINT_MAX - config->data_offset) ||
- immutableMemory.second < config->data_offset ||
- config->data_size >
- (immutableMemory.second - config->data_offset)) {
- ALOGE("startRecognition() data_size is too big");
- return BAD_VALUE;
- }
-
- AutoMutex lock(mLock);
- if (mServiceState == SOUND_TRIGGER_STATE_DISABLED) {
- return INVALID_OPERATION;
- }
- sp<Model> model = getModel(handle);
- if (model == 0) {
- return BAD_VALUE;
- }
-
- if (model->mState == Model::STATE_ACTIVE) {
- return INVALID_OPERATION;
- }
-
-
- //TODO: get capture handle and device from audio policy service
- config->capture_handle = model->mCaptureIOHandle;
- config->capture_device = model->mCaptureDevice;
- status_t status = mHalInterface->startRecognition(handle, config,
- SoundTriggerHwService::recognitionCallback,
- this);
-
- if (status == NO_ERROR) {
- model->mState = Model::STATE_ACTIVE;
- model->mConfig = *config;
- }
-
- return status;
-}
-
-status_t SoundTriggerHwService::Module::stopRecognition(sound_model_handle_t handle)
-{
- ALOGV("stopRecognition() model handle %d", handle);
- if (mHalInterface == 0) {
- return NO_INIT;
- }
- AutoMutex lock(mLock);
- sp<Model> model = getModel(handle);
- if (model == 0) {
- return BAD_VALUE;
- }
-
- if (model->mState != Model::STATE_ACTIVE) {
- return INVALID_OPERATION;
- }
- mHalInterface->stopRecognition(handle);
- model->mState = Model::STATE_IDLE;
- return NO_ERROR;
-}
-
-status_t SoundTriggerHwService::Module::getModelState(sound_model_handle_t handle)
-{
- ALOGV("getModelState() model handle %d", handle);
- if (mHalInterface == 0) {
- return NO_INIT;
- }
- AutoMutex lock(mLock);
- sp<Model> model = getModel(handle);
- if (model == 0) {
- return BAD_VALUE;
- }
-
- if (model->mState != Model::STATE_ACTIVE) {
- return INVALID_OPERATION;
- }
-
- return mHalInterface->getModelState(handle);
-}
-
-status_t SoundTriggerHwService::Module::setParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t param,
- int32_t value)
-{
- ALOGV("setParameter() handle=%d, param=%d, value=%d", handle, param, value);
- if (mHalInterface == 0) {
- return NO_INIT;
- }
-
- AutoMutex lock(mLock);
- return mHalInterface->setParameter(handle, param, value);
-}
-
-status_t SoundTriggerHwService::Module::getParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t param,
- int32_t* value)
-{
- ALOGV("getParameter() handle=%d, param=%d", handle, param);
- if (mHalInterface == 0) {
- return NO_INIT;
- }
-
- AutoMutex lock(mLock);
- return mHalInterface->getParameter(handle, param, value);
-}
-
-status_t SoundTriggerHwService::Module::queryParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t param,
- sound_trigger_model_parameter_range_t* param_range)
-{
- ALOGV("queryParameter() handle=%d, param=%d", handle, param);
- if (mHalInterface == 0) {
- return NO_INIT;
- }
-
- AutoMutex lock(mLock);
- return mHalInterface->queryParameter(handle, param, param_range);
-}
-
-void SoundTriggerHwService::Module::onCallbackEvent(const sp<CallbackEvent>& event)
-{
- ALOGV("onCallbackEvent type %d", event->mType);
-
- // Memory is coming from a trusted process.
- sp<IMemory> eventMemory = event->mMemory;
-
- if (eventMemory == 0 || eventMemory->unsecurePointer() == NULL) {
- return;
- }
- if (mModuleClients.isEmpty()) {
- ALOGI("%s no clients", __func__);
- return;
- }
-
- Vector< sp<ModuleClient> > clients;
-
- switch (event->mType) {
- case CallbackEvent::TYPE_RECOGNITION: {
- struct sound_trigger_recognition_event *recognitionEvent =
- (struct sound_trigger_recognition_event *)eventMemory->unsecurePointer();
- {
- AutoMutex lock(mLock);
- sp<Model> model = getModel(recognitionEvent->model);
- if (model == 0) {
- ALOGW("%s model == 0", __func__);
- return;
- }
- if (model->mState != Model::STATE_ACTIVE) {
- ALOGV("onCallbackEvent model->mState %d != Model::STATE_ACTIVE", model->mState);
- return;
- }
-
- recognitionEvent->capture_session = model->mCaptureSession;
- model->mState = Model::STATE_IDLE;
- clients.add(model->mModuleClient);
- }
- } break;
- case CallbackEvent::TYPE_SOUNDMODEL: {
- struct sound_trigger_model_event *soundmodelEvent =
- (struct sound_trigger_model_event *)eventMemory->unsecurePointer();
- {
- AutoMutex lock(mLock);
- sp<Model> model = getModel(soundmodelEvent->model);
- if (model == 0) {
- ALOGW("%s model == 0", __func__);
- return;
- }
- clients.add(model->mModuleClient);
- }
- } break;
- case CallbackEvent::TYPE_SERVICE_STATE: {
- {
- AutoMutex lock(mLock);
- for (size_t i = 0; i < mModuleClients.size(); i++) {
- if (mModuleClients[i] != 0) {
- clients.add(mModuleClients[i]);
- }
- }
- }
- } break;
- default:
- LOG_ALWAYS_FATAL("onCallbackEvent unknown event type %d", event->mType);
- }
-
- for (size_t i = 0; i < clients.size(); i++) {
- clients[i]->onCallbackEvent(event);
- }
-}
-
-sp<SoundTriggerHwService::Model> SoundTriggerHwService::Module::getModel(
- sound_model_handle_t handle)
-{
- sp<Model> model;
- ssize_t index = mModels.indexOfKey(handle);
- if (index >= 0) {
- model = mModels.valueAt(index);
- }
- return model;
-}
-
-// Called with mServiceLock held
-void SoundTriggerHwService::Module::setCaptureState_l(bool active)
-{
- ALOGV("Module::setCaptureState_l %d", active);
- sp<SoundTriggerHwService> service;
- sound_trigger_service_state_t state;
-
- Vector< sp<IMemory> > events;
- {
- AutoMutex lock(mLock);
- state = (active && !mDescriptor.properties.concurrent_capture) ?
- SOUND_TRIGGER_STATE_DISABLED : SOUND_TRIGGER_STATE_ENABLED;
-
- if (state == mServiceState) {
- return;
- }
-
- mServiceState = state;
-
- service = mService.promote();
- if (service == 0) {
- return;
- }
-
- if (state == SOUND_TRIGGER_STATE_ENABLED) {
- goto exit;
- }
-
- const bool supports_stop_all =
- (mHalInterface != 0) && (mHalInterface->stopAllRecognitions() != -ENOSYS);
-
- for (size_t i = 0; i < mModels.size(); i++) {
- sp<Model> model = mModels.valueAt(i);
- if (model->mState == Model::STATE_ACTIVE) {
- if (mHalInterface != 0 && !supports_stop_all) {
- mHalInterface->stopRecognition(model->mHandle);
- }
- // keep model in ACTIVE state so that event is processed by onCallbackEvent()
- if (model->mType == SOUND_MODEL_TYPE_KEYPHRASE) {
- struct sound_trigger_phrase_recognition_event event;
- memset(&event, 0, sizeof(struct sound_trigger_phrase_recognition_event));
- event.num_phrases = model->mConfig.num_phrases;
- for (size_t i = 0; i < event.num_phrases; i++) {
- event.phrase_extras[i] = model->mConfig.phrases[i];
- }
- event.common.status = RECOGNITION_STATUS_ABORT;
- event.common.type = model->mType;
- event.common.model = model->mHandle;
- event.common.data_size = 0;
- sp<IMemory> eventMemory = service->prepareRecognitionEvent(&event.common);
- if (eventMemory != 0) {
- events.add(eventMemory);
- }
- } else if (model->mType == SOUND_MODEL_TYPE_GENERIC) {
- struct sound_trigger_generic_recognition_event event;
- memset(&event, 0, sizeof(struct sound_trigger_generic_recognition_event));
- event.common.status = RECOGNITION_STATUS_ABORT;
- event.common.type = model->mType;
- event.common.model = model->mHandle;
- event.common.data_size = 0;
- sp<IMemory> eventMemory = service->prepareRecognitionEvent(&event.common);
- if (eventMemory != 0) {
- events.add(eventMemory);
- }
- } else if (model->mType == SOUND_MODEL_TYPE_UNKNOWN) {
- struct sound_trigger_phrase_recognition_event event;
- memset(&event, 0, sizeof(struct sound_trigger_phrase_recognition_event));
- event.common.status = RECOGNITION_STATUS_ABORT;
- event.common.type = model->mType;
- event.common.model = model->mHandle;
- event.common.data_size = 0;
- sp<IMemory> eventMemory = service->prepareRecognitionEvent(&event.common);
- if (eventMemory != 0) {
- events.add(eventMemory);
- }
- } else {
- goto exit;
- }
- }
- }
- }
-
- for (size_t i = 0; i < events.size(); i++) {
- sp<CallbackEvent> callbackEvent = new CallbackEvent(CallbackEvent::TYPE_RECOGNITION,
- events[i]);
- callbackEvent->setModule(this);
- service->sendCallbackEvent(callbackEvent);
- }
-
-exit:
- service->sendServiceStateEvent(state, this);
-}
-
-
-SoundTriggerHwService::Model::Model(sound_model_handle_t handle, audio_session_t session,
- audio_io_handle_t ioHandle, audio_devices_t device,
- sound_trigger_sound_model_type_t type,
- sp<ModuleClient>& moduleClient) :
- mHandle(handle), mState(STATE_IDLE), mCaptureSession(session),
- mCaptureIOHandle(ioHandle), mCaptureDevice(device), mType(type),
- mModuleClient(moduleClient)
-{
-}
-
-#undef LOG_TAG
-#define LOG_TAG "SoundTriggerHwService::ModuleClient"
-
-SoundTriggerHwService::ModuleClient::ModuleClient(const sp<Module>& module,
- const sp<ISoundTriggerClient>& client,
- const String16& opPackageName)
- : mModule(module), mClient(client), mOpPackageName(opPackageName)
-{
-}
-
-void SoundTriggerHwService::ModuleClient::onFirstRef()
-{
- sp<IBinder> binder = IInterface::asBinder(mClient);
- if (binder != 0) {
- binder->linkToDeath(this);
- }
-}
-
-SoundTriggerHwService::ModuleClient::~ModuleClient()
-{
-}
-
-status_t SoundTriggerHwService::ModuleClient::dump(int fd __unused,
- const Vector<String16>& args __unused) {
- String8 result;
- return NO_ERROR;
-}
-
-void SoundTriggerHwService::ModuleClient::detach() {
- ALOGV("detach()");
- if (!captureHotwordAllowed(mOpPackageName,
- IPCThreadState::self()->getCallingPid(),
- IPCThreadState::self()->getCallingUid())) {
- return;
- }
-
- {
- AutoMutex lock(mLock);
- if (mClient != 0) {
- IInterface::asBinder(mClient)->unlinkToDeath(this);
- mClient.clear();
- }
- }
-
- sp<Module> module = mModule.promote();
- if (module == 0) {
- return;
- }
- module->detach(this);
-}
-
-status_t SoundTriggerHwService::ModuleClient::loadSoundModel(const sp<IMemory>& modelMemory,
- sound_model_handle_t *handle)
-{
- ALOGV("loadSoundModel() handle");
- if (!captureHotwordAllowed(mOpPackageName,
- IPCThreadState::self()->getCallingPid(),
- IPCThreadState::self()->getCallingUid())) {
- return PERMISSION_DENIED;
- }
- if (checkIMemory(modelMemory) != NO_ERROR) {
- return BAD_VALUE;
- }
-
- sp<Module> module = mModule.promote();
- if (module == 0) {
- return NO_INIT;
- }
- return module->loadSoundModel(modelMemory, this, handle);
-}
-
-status_t SoundTriggerHwService::ModuleClient::unloadSoundModel(sound_model_handle_t handle)
-{
- ALOGV("unloadSoundModel() model handle %d", handle);
- if (!captureHotwordAllowed(mOpPackageName,
- IPCThreadState::self()->getCallingPid(),
- IPCThreadState::self()->getCallingUid())) {
- return PERMISSION_DENIED;
- }
-
- sp<Module> module = mModule.promote();
- if (module == 0) {
- return NO_INIT;
- }
- return module->unloadSoundModel(handle);
-}
-
-status_t SoundTriggerHwService::ModuleClient::startRecognition(sound_model_handle_t handle,
- const sp<IMemory>& dataMemory)
-{
- ALOGV("startRecognition() model handle %d", handle);
- if (!captureHotwordAllowed(mOpPackageName,
- IPCThreadState::self()->getCallingPid(),
- IPCThreadState::self()->getCallingUid())) {
- return PERMISSION_DENIED;
- }
- if (checkIMemory(dataMemory) != NO_ERROR) {
- return BAD_VALUE;
- }
-
- sp<Module> module = mModule.promote();
- if (module == 0) {
- return NO_INIT;
- }
- return module->startRecognition(handle, dataMemory);
-}
-
-status_t SoundTriggerHwService::ModuleClient::stopRecognition(sound_model_handle_t handle)
-{
- ALOGV("stopRecognition() model handle %d", handle);
- if (!captureHotwordAllowed(mOpPackageName,
- IPCThreadState::self()->getCallingPid(),
- IPCThreadState::self()->getCallingUid())) {
- return PERMISSION_DENIED;
- }
-
- sp<Module> module = mModule.promote();
- if (module == 0) {
- return NO_INIT;
- }
- return module->stopRecognition(handle);
-}
-
-status_t SoundTriggerHwService::ModuleClient::getModelState(sound_model_handle_t handle)
-{
- ALOGV("getModelState() model handle %d", handle);
- if (!captureHotwordAllowed(mOpPackageName,
- IPCThreadState::self()->getCallingPid(),
- IPCThreadState::self()->getCallingUid())) {
- return PERMISSION_DENIED;
- }
-
- sp<Module> module = mModule.promote();
- if (module == 0) {
- return NO_INIT;
- }
- return module->getModelState(handle);
-}
-
-status_t SoundTriggerHwService::ModuleClient::setParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t param, int32_t value)
-{
- ALOGV("setParameter() handle=%d, param=%d, value=%d", handle, param, value);
- if (!captureHotwordAllowed(mOpPackageName,
- IPCThreadState::self()->getCallingPid(),
- IPCThreadState::self()->getCallingUid())) {
- return PERMISSION_DENIED;
- }
-
- sp<Module> module = mModule.promote();
- if (module == 0) {
- return NO_INIT;
- }
- return module->setParameter(handle, param, value);
-}
-
-status_t SoundTriggerHwService::ModuleClient::getParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t param, int32_t* value)
-{
- ALOGV("getParameter() handle=%d, param=%d", handle, param);
- if (!captureHotwordAllowed(mOpPackageName,
- IPCThreadState::self()->getCallingPid(),
- IPCThreadState::self()->getCallingUid())) {
- return PERMISSION_DENIED;
- }
-
- sp<Module> module = mModule.promote();
- if (module == 0) {
- return NO_INIT;
- }
- return module->getParameter(handle, param, value);
-}
-
-status_t SoundTriggerHwService::ModuleClient::queryParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t param,
- sound_trigger_model_parameter_range_t* param_range)
-{
- ALOGV("isParameterSupported() handle=%d, param=%d", handle, param);
- if (!captureHotwordAllowed(mOpPackageName,
- IPCThreadState::self()->getCallingPid(),
- IPCThreadState::self()->getCallingUid())) {
- return PERMISSION_DENIED;
- }
-
- sp<Module> module = mModule.promote();
- if (module == 0) {
- return NO_INIT;
- }
- return module->queryParameter(handle, param, param_range);
-}
-
-void SoundTriggerHwService::ModuleClient::setCaptureState_l(bool active)
-{
- ALOGV("ModuleClient::setCaptureState_l %d", active);
- sp<SoundTriggerHwService> service;
- sound_trigger_service_state_t state;
-
- sp<Module> module = mModule.promote();
- if (module == 0) {
- return;
- }
- {
- AutoMutex lock(mLock);
- state = (active && !module->isConcurrentCaptureAllowed()) ?
- SOUND_TRIGGER_STATE_DISABLED : SOUND_TRIGGER_STATE_ENABLED;
-
- service = module->service().promote();
- if (service == 0) {
- return;
- }
- }
- service->sendServiceStateEvent(state, this);
-}
-
-void SoundTriggerHwService::ModuleClient::onCallbackEvent(const sp<CallbackEvent>& event)
-{
- ALOGV("ModuleClient onCallbackEvent type %d", event->mType);
-
- sp<IMemory> eventMemory = event->mMemory;
-
- // Memory is coming from a trusted process.
- if (eventMemory == 0 || eventMemory->unsecurePointer() == NULL) {
- return;
- }
-
- sp<ISoundTriggerClient> client;
- {
- AutoMutex lock(mLock);
- client = mClient;
- }
-
- if (client != 0) {
- switch (event->mType) {
- case CallbackEvent::TYPE_RECOGNITION: {
- client->onRecognitionEvent(eventMemory);
- } break;
- case CallbackEvent::TYPE_SOUNDMODEL: {
- client->onSoundModelEvent(eventMemory);
- } break;
- case CallbackEvent::TYPE_SERVICE_STATE: {
- client->onServiceStateChange(eventMemory);
- } break;
- default:
- LOG_ALWAYS_FATAL("onCallbackEvent unknown event type %d", event->mType);
- }
- }
-}
-
-void SoundTriggerHwService::ModuleClient::binderDied(
- const wp<IBinder> &who __unused) {
- ALOGW("client binder died for client %p", this);
- detach();
-}
-
-}; // namespace android
diff --git a/services/soundtrigger/SoundTriggerHwService.h b/services/soundtrigger/SoundTriggerHwService.h
deleted file mode 100644
index 4057e14..0000000
--- a/services/soundtrigger/SoundTriggerHwService.h
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_SOUNDTRIGGER_HAL_SERVICE_H
-#define ANDROID_HARDWARE_SOUNDTRIGGER_HAL_SERVICE_H
-
-#include <utils/Vector.h>
-//#include <binder/AppOpsManager.h>
-#include <binder/MemoryDealer.h>
-#include <binder/BinderService.h>
-#include <binder/IAppOpsCallback.h>
-#include <soundtrigger/ISoundTriggerHwService.h>
-#include <soundtrigger/ISoundTrigger.h>
-#include <soundtrigger/ISoundTriggerClient.h>
-#include <system/sound_trigger.h>
-#include "SoundTriggerHalInterface.h"
-
-namespace android {
-
-class MemoryHeapBase;
-
-class SoundTriggerHwService :
- public BinderService<SoundTriggerHwService>,
- public BnSoundTriggerHwService
-{
- friend class BinderService<SoundTriggerHwService>;
-public:
- class Module;
- class ModuleClient;
-
- static char const* getServiceName() { return "media.sound_trigger_hw"; }
-
- SoundTriggerHwService();
- virtual ~SoundTriggerHwService();
-
- // ISoundTriggerHwService
- virtual status_t listModules(const String16& opPackageName,
- struct sound_trigger_module_descriptor *modules,
- uint32_t *numModules);
-
- virtual status_t attach(const String16& opPackageName,
- const sound_trigger_module_handle_t handle,
- const sp<ISoundTriggerClient>& client,
- sp<ISoundTrigger>& module);
-
- virtual status_t setCaptureState(bool active);
-
- virtual status_t onTransact(uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags);
-
- virtual status_t dump(int fd, const Vector<String16>& args);
-
- class Model : public RefBase {
- public:
-
- enum {
- STATE_IDLE,
- STATE_ACTIVE
- };
-
- Model(sound_model_handle_t handle, audio_session_t session, audio_io_handle_t ioHandle,
- audio_devices_t device, sound_trigger_sound_model_type_t type,
- sp<ModuleClient>& moduleClient);
- ~Model() {}
-
- sound_model_handle_t mHandle;
- int mState;
- audio_session_t mCaptureSession;
- audio_io_handle_t mCaptureIOHandle;
- audio_devices_t mCaptureDevice;
- sound_trigger_sound_model_type_t mType;
- struct sound_trigger_recognition_config mConfig;
- sp<ModuleClient> mModuleClient;
- };
-
- class CallbackEvent : public RefBase {
- public:
- typedef enum {
- TYPE_RECOGNITION,
- TYPE_SOUNDMODEL,
- TYPE_SERVICE_STATE,
- } event_type;
- CallbackEvent(event_type type, sp<IMemory> memory);
-
- virtual ~CallbackEvent();
-
- void setModule(wp<Module> module) { mModule = module; }
- void setModuleClient(wp<ModuleClient> moduleClient) { mModuleClient = moduleClient; }
-
- event_type mType;
- sp<IMemory> mMemory;
- wp<Module> mModule;
- wp<ModuleClient> mModuleClient;
- };
-
- class Module : public RefBase {
- public:
-
- Module(const sp<SoundTriggerHwService>& service,
- const sp<SoundTriggerHalInterface>& halInterface,
- sound_trigger_module_descriptor descriptor);
-
- virtual ~Module();
-
- virtual status_t loadSoundModel(const sp<IMemory>& modelMemory,
- sp<ModuleClient> moduleClient,
- sound_model_handle_t *handle);
-
- virtual status_t unloadSoundModel(sound_model_handle_t handle);
-
- virtual status_t startRecognition(sound_model_handle_t handle,
- const sp<IMemory>& dataMemory);
- virtual status_t stopRecognition(sound_model_handle_t handle);
- virtual status_t getModelState(sound_model_handle_t handle);
- virtual status_t setParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t param,
- int32_t value);
- virtual status_t getParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t param,
- int32_t* value);
- virtual status_t queryParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t param,
- sound_trigger_model_parameter_range_t* param_range);
-
- sp<SoundTriggerHalInterface> halInterface() const { return mHalInterface; }
- struct sound_trigger_module_descriptor descriptor() { return mDescriptor; }
- wp<SoundTriggerHwService> service() const { return mService; }
- bool isConcurrentCaptureAllowed() const { return mDescriptor.properties.concurrent_capture; }
-
- sp<Model> getModel(sound_model_handle_t handle);
-
- void setCaptureState_l(bool active);
-
- sp<ModuleClient> addClient(const sp<ISoundTriggerClient>& client,
- const String16& opPackageName);
-
- void detach(const sp<ModuleClient>& moduleClient);
-
- void onCallbackEvent(const sp<CallbackEvent>& event);
-
- private:
-
- Mutex mLock;
- wp<SoundTriggerHwService> mService;
- sp<SoundTriggerHalInterface> mHalInterface;
- struct sound_trigger_module_descriptor mDescriptor;
- Vector< sp<ModuleClient> > mModuleClients;
- DefaultKeyedVector< sound_model_handle_t, sp<Model> > mModels;
- sound_trigger_service_state_t mServiceState;
- }; // class Module
-
- class ModuleClient : public virtual RefBase,
- public BnSoundTrigger,
- public IBinder::DeathRecipient {
- public:
-
- ModuleClient(const sp<Module>& module,
- const sp<ISoundTriggerClient>& client,
- const String16& opPackageName);
-
- virtual ~ModuleClient();
-
- virtual void detach();
-
- virtual status_t loadSoundModel(const sp<IMemory>& modelMemory,
- sound_model_handle_t *handle);
-
- virtual status_t unloadSoundModel(sound_model_handle_t handle);
-
- virtual status_t startRecognition(sound_model_handle_t handle,
- const sp<IMemory>& dataMemory);
- virtual status_t stopRecognition(sound_model_handle_t handle);
- virtual status_t getModelState(sound_model_handle_t handle);
- virtual status_t setParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t param,
- int32_t value);
- virtual status_t getParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t param,
- int32_t* value);
- virtual status_t queryParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t param,
- sound_trigger_model_parameter_range_t* param_range);
-
- virtual status_t dump(int fd, const Vector<String16>& args);
-
- virtual void onFirstRef();
-
- // IBinder::DeathRecipient implementation
- virtual void binderDied(const wp<IBinder> &who);
-
- void onCallbackEvent(const sp<CallbackEvent>& event);
-
- void setCaptureState_l(bool active);
-
- sp<ISoundTriggerClient> client() const { return mClient; }
-
- private:
-
- mutable Mutex mLock;
- wp<Module> mModule;
- sp<ISoundTriggerClient> mClient;
- const String16 mOpPackageName;
- }; // class ModuleClient
-
- class CallbackThread : public Thread {
- public:
-
- explicit CallbackThread(const wp<SoundTriggerHwService>& service);
-
- virtual ~CallbackThread();
-
- // Thread virtuals
- virtual bool threadLoop();
-
- // RefBase
- virtual void onFirstRef();
-
- void exit();
- void sendCallbackEvent(const sp<CallbackEvent>& event);
-
- private:
- wp<SoundTriggerHwService> mService;
- Condition mCallbackCond;
- Mutex mCallbackLock;
- Vector< sp<CallbackEvent> > mEventQueue;
- };
-
- static void recognitionCallback(struct sound_trigger_recognition_event *event, void *cookie);
- sp<IMemory> prepareRecognitionEvent(struct sound_trigger_recognition_event *event);
- void sendRecognitionEvent(struct sound_trigger_recognition_event *event, Module *module);
-
- static void soundModelCallback(struct sound_trigger_model_event *event, void *cookie);
- sp<IMemory> prepareSoundModelEvent(struct sound_trigger_model_event *event);
- void sendSoundModelEvent(struct sound_trigger_model_event *event, Module *module);
-
- sp<IMemory> prepareServiceStateEvent(sound_trigger_service_state_t state);
- void sendServiceStateEvent(sound_trigger_service_state_t state, Module *module);
- void sendServiceStateEvent(sound_trigger_service_state_t state,
- ModuleClient *moduleClient);
-
- void sendCallbackEvent(const sp<CallbackEvent>& event);
- void onCallbackEvent(const sp<CallbackEvent>& event);
-
-private:
-
- virtual void onFirstRef();
-
- Mutex mServiceLock;
- volatile int32_t mNextUniqueId;
- DefaultKeyedVector< sound_trigger_module_handle_t, sp<Module> > mModules;
- sp<CallbackThread> mCallbackThread;
- sp<MemoryDealer> mMemoryDealer;
- Mutex mMemoryDealerLock;
- bool mCaptureState;
-};
-
-} // namespace android
-
-#endif // ANDROID_HARDWARE_SOUNDTRIGGER_HAL_SERVICE_H
diff --git a/soundtrigger/Android.bp b/soundtrigger/Android.bp
deleted file mode 100644
index 6178153..0000000
--- a/soundtrigger/Android.bp
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2014 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.
-
-cc_library_shared {
- name: "libsoundtrigger",
-
- srcs: [
- "SoundTrigger.cpp",
- "ISoundTrigger.cpp",
- "ISoundTriggerClient.cpp",
- "ISoundTriggerHwService.cpp",
- ],
-
- shared_libs: [
- "libcutils",
- "libutils",
- "liblog",
- "libbinder",
- ],
-
- cflags: [
- "-Werror",
- "-Wall",
- ],
-}
diff --git a/soundtrigger/ISoundTrigger.cpp b/soundtrigger/ISoundTrigger.cpp
deleted file mode 100644
index 9b1da13..0000000
--- a/soundtrigger/ISoundTrigger.cpp
+++ /dev/null
@@ -1,319 +0,0 @@
-/*
-**
-** Copyright 2014, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#define LOG_TAG "ISoundTrigger"
-#include <utils/Log.h>
-#include <utils/Errors.h>
-#include <binder/IMemory.h>
-#include <soundtrigger/ISoundTrigger.h>
-#include <soundtrigger/ISoundTriggerHwService.h>
-#include <soundtrigger/ISoundTriggerClient.h>
-#include <system/sound_trigger.h>
-
-namespace android {
-
-enum {
- DETACH = IBinder::FIRST_CALL_TRANSACTION,
- LOAD_SOUND_MODEL,
- UNLOAD_SOUND_MODEL,
- START_RECOGNITION,
- STOP_RECOGNITION,
- GET_MODEL_STATE,
- SET_PARAMETER,
- GET_PARAMETER,
- QUERY_PARAMETER,
-};
-
-class BpSoundTrigger: public BpInterface<ISoundTrigger>
-{
-public:
- explicit BpSoundTrigger(const sp<IBinder>& impl)
- : BpInterface<ISoundTrigger>(impl)
- {
- }
-
- void detach()
- {
- ALOGV("detach");
- Parcel data, reply;
- data.writeInterfaceToken(ISoundTrigger::getInterfaceDescriptor());
- remote()->transact(DETACH, data, &reply);
- }
-
- status_t loadSoundModel(const sp<IMemory>& modelMemory,
- sound_model_handle_t *handle)
- {
- if (modelMemory == 0 || handle == NULL) {
- return BAD_VALUE;
- }
- Parcel data, reply;
- data.writeInterfaceToken(ISoundTrigger::getInterfaceDescriptor());
- data.writeStrongBinder(IInterface::asBinder(modelMemory));
- status_t status = remote()->transact(LOAD_SOUND_MODEL, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- status = (status_t)reply.readInt32();
- if (status == NO_ERROR) {
- reply.read(handle, sizeof(sound_model_handle_t));
- }
- return status;
- }
-
- virtual status_t unloadSoundModel(sound_model_handle_t handle)
- {
- Parcel data, reply;
- data.writeInterfaceToken(ISoundTrigger::getInterfaceDescriptor());
- data.write(&handle, sizeof(sound_model_handle_t));
- status_t status = remote()->transact(UNLOAD_SOUND_MODEL, data, &reply);
- if (status == NO_ERROR) {
- status = (status_t)reply.readInt32();
- }
- return status;
- }
-
- virtual status_t startRecognition(sound_model_handle_t handle,
- const sp<IMemory>& dataMemory)
- {
- Parcel data, reply;
- data.writeInterfaceToken(ISoundTrigger::getInterfaceDescriptor());
- data.write(&handle, sizeof(sound_model_handle_t));
- if (dataMemory == 0) {
- data.writeInt32(0);
- } else {
- data.writeInt32(dataMemory->size());
- }
- data.writeStrongBinder(IInterface::asBinder(dataMemory));
- status_t status = remote()->transact(START_RECOGNITION, data, &reply);
- if (status == NO_ERROR) {
- status = (status_t)reply.readInt32();
- }
- return status;
- }
-
- virtual status_t stopRecognition(sound_model_handle_t handle)
- {
- Parcel data, reply;
- data.writeInterfaceToken(ISoundTrigger::getInterfaceDescriptor());
- data.write(&handle, sizeof(sound_model_handle_t));
- status_t status = remote()->transact(STOP_RECOGNITION, data, &reply);
- if (status == NO_ERROR) {
- status = (status_t)reply.readInt32();
- }
- return status;
- }
-
- virtual status_t getModelState(sound_model_handle_t handle)
- {
- Parcel data, reply;
- data.writeInterfaceToken(ISoundTrigger::getInterfaceDescriptor());
- data.write(&handle, sizeof(sound_model_handle_t));
- status_t status = remote()->transact(GET_MODEL_STATE, data, &reply);
- if (status == NO_ERROR) {
- status = (status_t)reply.readInt32();
- }
- return status;
- }
-
- virtual status_t setParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t param,
- int32_t value)
- {
- Parcel data, reply;
- data.writeInterfaceToken(ISoundTrigger::getInterfaceDescriptor());
- data.write(&handle, sizeof(sound_model_handle_t));
- data.write(¶m, sizeof(sound_trigger_model_parameter_t));
- data.writeInt32(value);
- status_t status = remote()->transact(SET_PARAMETER, data, &reply);
- if (status == NO_ERROR) {
- status = (status_t)reply.readInt32();
- }
- return status;
- }
-
- virtual status_t getParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t param,
- int32_t* value)
- {
- Parcel data, reply;
- data.writeInterfaceToken(ISoundTrigger::getInterfaceDescriptor());
- data.write(&handle, sizeof(sound_model_handle_t));
- data.write(¶m, sizeof(sound_trigger_model_parameter_t));
- status_t status = remote()->transact(GET_PARAMETER, data, &reply);
- if (status == NO_ERROR) {
- status = (status_t)reply.readInt32();
- *value = reply.readInt32();
- }
- return status;
- }
-
- virtual status_t queryParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t param,
- sound_trigger_model_parameter_range_t* param_range)
- {
- Parcel data, reply;
- data.writeInterfaceToken(ISoundTrigger::getInterfaceDescriptor());
- data.write(&handle, sizeof(sound_model_handle_t));
- data.write(¶m, sizeof(sound_trigger_model_parameter_t));
- status_t status = remote()->transact(QUERY_PARAMETER, data, &reply);
- if (status == NO_ERROR) {
- status = (status_t)reply.readInt32();
- param_range->start = reply.readInt32();
- param_range->end = reply.readInt32();
- }
- return status;
- }
-
-};
-
-IMPLEMENT_META_INTERFACE(SoundTrigger, "android.hardware.ISoundTrigger");
-
-// ----------------------------------------------------------------------
-
-status_t BnSoundTrigger::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch(code) {
- case DETACH: {
- ALOGV("DETACH");
- CHECK_INTERFACE(ISoundTrigger, data, reply);
- detach();
- return NO_ERROR;
- } break;
- case LOAD_SOUND_MODEL: {
- CHECK_INTERFACE(ISoundTrigger, data, reply);
- sp<IMemory> modelMemory = interface_cast<IMemory>(
- data.readStrongBinder());
- sound_model_handle_t handle;
- status_t status = loadSoundModel(modelMemory, &handle);
- reply->writeInt32(status);
- if (status == NO_ERROR) {
- reply->write(&handle, sizeof(sound_model_handle_t));
- }
- return NO_ERROR;
- }
- case UNLOAD_SOUND_MODEL: {
- CHECK_INTERFACE(ISoundTrigger, data, reply);
- sound_model_handle_t handle;
- data.read(&handle, sizeof(sound_model_handle_t));
- status_t status = unloadSoundModel(handle);
- reply->writeInt32(status);
- return NO_ERROR;
- }
- case START_RECOGNITION: {
- CHECK_INTERFACE(ISoundTrigger, data, reply);
- sound_model_handle_t handle;
- data.read(&handle, sizeof(sound_model_handle_t));
- sp<IMemory> dataMemory;
- if (data.readInt32() != 0) {
- dataMemory = interface_cast<IMemory>(data.readStrongBinder());
- }
- status_t status = startRecognition(handle, dataMemory);
- reply->writeInt32(status);
- return NO_ERROR;
- }
- case STOP_RECOGNITION: {
- CHECK_INTERFACE(ISoundTrigger, data, reply);
- sound_model_handle_t handle;
- data.read(&handle, sizeof(sound_model_handle_t));
- status_t status = stopRecognition(handle);
- reply->writeInt32(status);
- return NO_ERROR;
- }
- case GET_MODEL_STATE: {
- CHECK_INTERFACE(ISoundTrigger, data, reply);
- sound_model_handle_t handle;
- status_t status = UNKNOWN_ERROR;
- status_t ret = data.read(&handle, sizeof(sound_model_handle_t));
- if (ret == NO_ERROR) {
- status = getModelState(handle);
- }
- reply->writeInt32(status);
- return ret;
- }
- case SET_PARAMETER: {
- CHECK_INTERFACE(ISoundTrigger, data, reply);
- sound_model_handle_t handle;
- sound_trigger_model_parameter_t param;
- int32_t value;
- status_t status = UNKNOWN_ERROR;
- status_t ret;
- ret = data.read(&handle, sizeof(sound_model_handle_t));
- if (ret != NO_ERROR) {
- return ret;
- }
- ret = data.read(¶m, sizeof(sound_trigger_model_parameter_t));
- if (ret != NO_ERROR) {
- return ret;
- }
- ret = data.read(&value, sizeof(int32_t));
- if (ret != NO_ERROR) {
- return ret;
- }
- status = setParameter(handle, param, value);
- reply->writeInt32(status);
- return NO_ERROR;
- }
- case GET_PARAMETER: {
- CHECK_INTERFACE(ISoundTrigger, data, reply);
- sound_model_handle_t handle;
- sound_trigger_model_parameter_t param;
- int32_t value;
- status_t status = UNKNOWN_ERROR;
- status_t ret;
- ret = data.read(&handle, sizeof(sound_model_handle_t));
- if (ret != NO_ERROR) {
- return ret;
- }
- ret = data.read(¶m, sizeof(sound_trigger_model_parameter_t));
- if (ret != NO_ERROR) {
- return ret;
- }
- status = getParameter(handle, param, &value);
- reply->writeInt32(status);
- reply->writeInt32(value);
- return NO_ERROR;
- }
- case QUERY_PARAMETER: {
- CHECK_INTERFACE(ISoundTrigger, data, reply);
- sound_model_handle_t handle;
- sound_trigger_model_parameter_t param;
- status_t ret;
- status_t status = UNKNOWN_ERROR;
- sound_trigger_model_parameter_range_t retValue;
- ret = data.read(&handle, sizeof(sound_model_handle_t));
- if (ret != NO_ERROR) {
- return ret;
- }
- ret = data.read(¶m, sizeof(sound_trigger_model_parameter_t));
- if (ret != NO_ERROR) {
- return ret;
- }
- status = queryParameter(handle, param, &retValue);
- reply->writeInt32(status);
- reply->writeInt32(retValue.start);
- reply->writeInt32(retValue.end);
- return NO_ERROR;
- }
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
diff --git a/soundtrigger/ISoundTriggerClient.cpp b/soundtrigger/ISoundTriggerClient.cpp
deleted file mode 100644
index 1385631..0000000
--- a/soundtrigger/ISoundTriggerClient.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
-**
-** Copyright 2014, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <binder/IMemory.h>
-#include <binder/Parcel.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <soundtrigger/ISoundTriggerClient.h>
-
-namespace android {
-
-enum {
- ON_RECOGNITION_EVENT = IBinder::FIRST_CALL_TRANSACTION,
- ON_SOUNDMODEL_EVENT,
- ON_SERVICE_STATE_CHANGE
-};
-
-class BpSoundTriggerClient: public BpInterface<ISoundTriggerClient>
-{
-
-public:
- explicit BpSoundTriggerClient(const sp<IBinder>& impl)
- : BpInterface<ISoundTriggerClient>(impl)
- {
- }
-
- virtual void onRecognitionEvent(const sp<IMemory>& eventMemory)
- {
- Parcel data, reply;
- data.writeInterfaceToken(ISoundTriggerClient::getInterfaceDescriptor());
- data.writeStrongBinder(IInterface::asBinder(eventMemory));
- remote()->transact(ON_RECOGNITION_EVENT,
- data,
- &reply);
- }
-
- virtual void onSoundModelEvent(const sp<IMemory>& eventMemory)
- {
- Parcel data, reply;
- data.writeInterfaceToken(ISoundTriggerClient::getInterfaceDescriptor());
- data.writeStrongBinder(IInterface::asBinder(eventMemory));
- remote()->transact(ON_SOUNDMODEL_EVENT,
- data,
- &reply);
- }
- virtual void onServiceStateChange(const sp<IMemory>& eventMemory)
- {
- Parcel data, reply;
- data.writeInterfaceToken(ISoundTriggerClient::getInterfaceDescriptor());
- data.writeStrongBinder(IInterface::asBinder(eventMemory));
- remote()->transact(ON_SERVICE_STATE_CHANGE,
- data,
- &reply);
- }
-};
-
-IMPLEMENT_META_INTERFACE(SoundTriggerClient,
- "android.hardware.ISoundTriggerClient");
-
-// ----------------------------------------------------------------------
-
-status_t BnSoundTriggerClient::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch(code) {
- case ON_RECOGNITION_EVENT: {
- CHECK_INTERFACE(ISoundTriggerClient, data, reply);
- sp<IMemory> eventMemory = interface_cast<IMemory>(
- data.readStrongBinder());
- onRecognitionEvent(eventMemory);
- return NO_ERROR;
- } break;
- case ON_SOUNDMODEL_EVENT: {
- CHECK_INTERFACE(ISoundTriggerClient, data, reply);
- sp<IMemory> eventMemory = interface_cast<IMemory>(
- data.readStrongBinder());
- onSoundModelEvent(eventMemory);
- return NO_ERROR;
- } break;
- case ON_SERVICE_STATE_CHANGE: {
- CHECK_INTERFACE(ISoundTriggerClient, data, reply);
- sp<IMemory> eventMemory = interface_cast<IMemory>(
- data.readStrongBinder());
- onServiceStateChange(eventMemory);
- return NO_ERROR;
- } break;
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
diff --git a/soundtrigger/ISoundTriggerHwService.cpp b/soundtrigger/ISoundTriggerHwService.cpp
deleted file mode 100644
index bd107b4..0000000
--- a/soundtrigger/ISoundTriggerHwService.cpp
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
-**
-** Copyright 2014, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#define LOG_TAG "BpSoundTriggerHwService"
-//#define LOG_NDEBUG 0
-
-#include <utils/Log.h>
-#include <utils/Errors.h>
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <binder/IMemory.h>
-#include <binder/Parcel.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-
-#include <soundtrigger/ISoundTriggerHwService.h>
-#include <soundtrigger/ISoundTrigger.h>
-#include <soundtrigger/ISoundTriggerClient.h>
-
-namespace android {
-
-enum {
- LIST_MODULES = IBinder::FIRST_CALL_TRANSACTION,
- ATTACH,
- SET_CAPTURE_STATE,
-};
-
-#define MAX_ITEMS_PER_LIST 1024
-
-class BpSoundTriggerHwService: public BpInterface<ISoundTriggerHwService>
-{
-public:
- explicit BpSoundTriggerHwService(const sp<IBinder>& impl)
- : BpInterface<ISoundTriggerHwService>(impl)
- {
- }
-
- virtual status_t listModules(const String16& opPackageName,
- struct sound_trigger_module_descriptor *modules,
- uint32_t *numModules)
- {
- if (numModules == NULL || (*numModules != 0 && modules == NULL)) {
- return BAD_VALUE;
- }
- Parcel data, reply;
- data.writeInterfaceToken(ISoundTriggerHwService::getInterfaceDescriptor());
- data.writeString16(opPackageName);
- unsigned int numModulesReq = (modules == NULL) ? 0 : *numModules;
- data.writeInt32(numModulesReq);
- status_t status = remote()->transact(LIST_MODULES, data, &reply);
- if (status == NO_ERROR) {
- status = (status_t)reply.readInt32();
- *numModules = (unsigned int)reply.readInt32();
- }
- ALOGV("listModules() status %d got *numModules %d", status, *numModules);
- if (status == NO_ERROR) {
- if (numModulesReq > *numModules) {
- numModulesReq = *numModules;
- }
- if (numModulesReq > 0) {
- reply.read(modules, numModulesReq * sizeof(struct sound_trigger_module_descriptor));
- }
- }
- return status;
- }
-
- virtual status_t attach(const String16& opPackageName,
- const sound_trigger_module_handle_t handle,
- const sp<ISoundTriggerClient>& client,
- sp<ISoundTrigger>& module)
- {
- Parcel data, reply;
- data.writeInterfaceToken(ISoundTriggerHwService::getInterfaceDescriptor());
- data.writeString16(opPackageName);
- data.write(&handle, sizeof(sound_trigger_module_handle_t));
- data.writeStrongBinder(IInterface::asBinder(client));
- status_t status = remote()->transact(ATTACH, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- status = reply.readInt32();
- if (reply.readInt32() != 0) {
- module = interface_cast<ISoundTrigger>(reply.readStrongBinder());
- }
- return status;
- }
-
- virtual status_t setCaptureState(bool active)
- {
- Parcel data, reply;
- data.writeInterfaceToken(ISoundTriggerHwService::getInterfaceDescriptor());
- data.writeInt32(active);
- status_t status = remote()->transact(SET_CAPTURE_STATE, data, &reply);
- if (status == NO_ERROR) {
- status = reply.readInt32();
- }
- return status;
- }
-
-};
-
-IMPLEMENT_META_INTERFACE(SoundTriggerHwService, "android.hardware.ISoundTriggerHwService");
-
-// ----------------------------------------------------------------------
-
-status_t BnSoundTriggerHwService::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch(code) {
- case LIST_MODULES: {
- CHECK_INTERFACE(ISoundTriggerHwService, data, reply);
- String16 opPackageName;
- status_t status = data.readString16(&opPackageName);
- if (status != NO_ERROR) {
- return status;
- }
- unsigned int numModulesReq = data.readInt32();
- if (numModulesReq > MAX_ITEMS_PER_LIST) {
- numModulesReq = MAX_ITEMS_PER_LIST;
- }
- unsigned int numModules = numModulesReq;
- struct sound_trigger_module_descriptor *modules =
- (struct sound_trigger_module_descriptor *)calloc(numModulesReq,
- sizeof(struct sound_trigger_module_descriptor));
- if (modules == NULL) {
- reply->writeInt32(NO_MEMORY);
- reply->writeInt32(0);
- return NO_ERROR;
- }
- status = listModules(opPackageName, modules, &numModules);
- reply->writeInt32(status);
- reply->writeInt32(numModules);
- ALOGV("LIST_MODULES status %d got numModules %d", status, numModules);
-
- if (status == NO_ERROR) {
- if (numModulesReq > numModules) {
- numModulesReq = numModules;
- }
- reply->write(modules,
- numModulesReq * sizeof(struct sound_trigger_module_descriptor));
- }
- free(modules);
- return NO_ERROR;
- }
-
- case ATTACH: {
- CHECK_INTERFACE(ISoundTriggerHwService, data, reply);
- String16 opPackageName;
- status_t status = data.readString16(&opPackageName);
- if (status != NO_ERROR) {
- return status;
- }
- sound_trigger_module_handle_t handle;
- data.read(&handle, sizeof(sound_trigger_module_handle_t));
- sp<ISoundTriggerClient> client =
- interface_cast<ISoundTriggerClient>(data.readStrongBinder());
- sp<ISoundTrigger> module;
- status = attach(opPackageName, handle, client, module);
- reply->writeInt32(status);
- if (module != 0) {
- reply->writeInt32(1);
- reply->writeStrongBinder(IInterface::asBinder(module));
- } else {
- reply->writeInt32(0);
- }
- return NO_ERROR;
- } break;
-
- case SET_CAPTURE_STATE: {
- CHECK_INTERFACE(ISoundTriggerHwService, data, reply);
- reply->writeInt32(setCaptureState((bool)data.readInt32()));
- return NO_ERROR;
- } break;
-
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
diff --git a/soundtrigger/OWNERS b/soundtrigger/OWNERS
deleted file mode 100644
index e83f6b9..0000000
--- a/soundtrigger/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-elaurent@google.com
-thorntonc@google.com
diff --git a/soundtrigger/SoundTrigger.cpp b/soundtrigger/SoundTrigger.cpp
deleted file mode 100644
index 021697b..0000000
--- a/soundtrigger/SoundTrigger.cpp
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
-**
-** Copyright (C) 2014, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#define LOG_TAG "SoundTrigger"
-//#define LOG_NDEBUG 0
-
-#include <utils/Log.h>
-#include <utils/threads.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/IMemory.h>
-
-#include <soundtrigger/SoundTrigger.h>
-#include <soundtrigger/ISoundTrigger.h>
-#include <soundtrigger/ISoundTriggerHwService.h>
-#include <soundtrigger/ISoundTriggerClient.h>
-#include <soundtrigger/SoundTriggerCallback.h>
-
-namespace android {
-
-namespace {
- sp<ISoundTriggerHwService> gSoundTriggerHwService;
- const int kSoundTriggerHwServicePollDelay = 500000; // 0.5s
- const char* kSoundTriggerHwServiceName = "media.sound_trigger_hw";
- Mutex gLock;
-
- class DeathNotifier : public IBinder::DeathRecipient
- {
- public:
- DeathNotifier() {
- }
-
- virtual void binderDied(const wp<IBinder>& who __unused) {
- ALOGV("binderDied");
- Mutex::Autolock _l(gLock);
- gSoundTriggerHwService.clear();
- ALOGW("Sound trigger service died!");
- }
- };
-
- sp<DeathNotifier> gDeathNotifier;
-}; // namespace anonymous
-
-const sp<ISoundTriggerHwService> SoundTrigger::getSoundTriggerHwService()
-{
- Mutex::Autolock _l(gLock);
- if (gSoundTriggerHwService.get() == 0) {
- sp<IServiceManager> sm = defaultServiceManager();
- sp<IBinder> binder;
- do {
- binder = sm->getService(String16(kSoundTriggerHwServiceName));
- if (binder != 0) {
- break;
- }
- ALOGW("SoundTriggerHwService not published, waiting...");
- usleep(kSoundTriggerHwServicePollDelay);
- } while(true);
- if (gDeathNotifier == NULL) {
- gDeathNotifier = new DeathNotifier();
- }
- binder->linkToDeath(gDeathNotifier);
- gSoundTriggerHwService = interface_cast<ISoundTriggerHwService>(binder);
- }
- ALOGE_IF(gSoundTriggerHwService == 0, "no SoundTriggerHwService!?");
- return gSoundTriggerHwService;
-}
-
-// Static methods
-status_t SoundTrigger::listModules(const String16& opPackageName,
- struct sound_trigger_module_descriptor *modules,
- uint32_t *numModules)
-{
- ALOGV("listModules()");
- const sp<ISoundTriggerHwService> service = getSoundTriggerHwService();
- if (service == 0) {
- return NO_INIT;
- }
- return service->listModules(opPackageName, modules, numModules);
-}
-
-sp<SoundTrigger> SoundTrigger::attach(const String16& opPackageName,
- const sound_trigger_module_handle_t module,
- const sp<SoundTriggerCallback>& callback)
-{
- ALOGV("attach()");
- sp<SoundTrigger> soundTrigger;
- const sp<ISoundTriggerHwService> service = getSoundTriggerHwService();
- if (service == 0) {
- return soundTrigger;
- }
- soundTrigger = new SoundTrigger(module, callback);
- status_t status = service->attach(opPackageName, module, soundTrigger,
- soundTrigger->mISoundTrigger);
-
- if (status == NO_ERROR && soundTrigger->mISoundTrigger != 0) {
- IInterface::asBinder(soundTrigger->mISoundTrigger)->linkToDeath(soundTrigger);
- } else {
- ALOGW("Error %d connecting to sound trigger service", status);
- soundTrigger.clear();
- }
- return soundTrigger;
-}
-
-
-status_t SoundTrigger::setCaptureState(bool active)
-{
- ALOGV("setCaptureState(%d)", active);
- const sp<ISoundTriggerHwService> service = getSoundTriggerHwService();
- if (service == 0) {
- return NO_INIT;
- }
- return service->setCaptureState(active);
-}
-
-// SoundTrigger
-SoundTrigger::SoundTrigger(sound_trigger_module_handle_t /*module*/,
- const sp<SoundTriggerCallback>& callback)
- : mCallback(callback)
-{
-}
-
-SoundTrigger::~SoundTrigger()
-{
- if (mISoundTrigger != 0) {
- mISoundTrigger->detach();
- }
-}
-
-
-void SoundTrigger::detach() {
- ALOGV("detach()");
- Mutex::Autolock _l(mLock);
- mCallback.clear();
- if (mISoundTrigger != 0) {
- mISoundTrigger->detach();
- IInterface::asBinder(mISoundTrigger)->unlinkToDeath(this);
- mISoundTrigger = 0;
- }
-}
-
-status_t SoundTrigger::loadSoundModel(const sp<IMemory>& modelMemory,
- sound_model_handle_t *handle)
-{
- Mutex::Autolock _l(mLock);
- if (mISoundTrigger == 0) {
- return NO_INIT;
- }
-
- return mISoundTrigger->loadSoundModel(modelMemory, handle);
-}
-
-status_t SoundTrigger::unloadSoundModel(sound_model_handle_t handle)
-{
- Mutex::Autolock _l(mLock);
- if (mISoundTrigger == 0) {
- return NO_INIT;
- }
- return mISoundTrigger->unloadSoundModel(handle);
-}
-
-status_t SoundTrigger::startRecognition(sound_model_handle_t handle,
- const sp<IMemory>& dataMemory)
-{
- Mutex::Autolock _l(mLock);
- if (mISoundTrigger == 0) {
- return NO_INIT;
- }
- return mISoundTrigger->startRecognition(handle, dataMemory);
-}
-
-status_t SoundTrigger::stopRecognition(sound_model_handle_t handle)
-{
- Mutex::Autolock _l(mLock);
- if (mISoundTrigger == 0) {
- return NO_INIT;
- }
- return mISoundTrigger->stopRecognition(handle);
-}
-
-status_t SoundTrigger::getModelState(sound_model_handle_t handle)
-{
- Mutex::Autolock _l(mLock);
- if (mISoundTrigger == 0) {
- return NO_INIT;
- }
- return mISoundTrigger->getModelState(handle);
-}
-
-status_t SoundTrigger::setParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t param, int32_t value)
-{
- Mutex::Autolock _l(mLock);
- if (mISoundTrigger == 0) {
- return NO_INIT;
- }
- return mISoundTrigger->setParameter(handle, param, value);
-}
-
-status_t SoundTrigger::getParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t param, int32_t* value)
-{
- Mutex::Autolock _l(mLock);
- if (mISoundTrigger == 0) {
- return NO_INIT;
- }
- return mISoundTrigger->getParameter(handle, param, value);
-}
-
-status_t SoundTrigger::queryParameter(sound_model_handle_t handle,
- sound_trigger_model_parameter_t param,
- sound_trigger_model_parameter_range_t* param_range)
-{
- Mutex::Autolock _l(mLock);
- if (mISoundTrigger == 0) {
- return NO_INIT;
- }
- return mISoundTrigger->queryParameter(handle, param, param_range);
-}
-
-// BpSoundTriggerClient
-void SoundTrigger::onRecognitionEvent(const sp<IMemory>& eventMemory)
-{
- Mutex::Autolock _l(mLock);
- if (eventMemory == 0 || eventMemory->unsecurePointer() == NULL) {
- return;
- }
-
- if (mCallback != 0) {
- // Memory is coming from a trusted process.
- mCallback->onRecognitionEvent(
- (struct sound_trigger_recognition_event *)eventMemory->unsecurePointer());
- }
-}
-
-void SoundTrigger::onSoundModelEvent(const sp<IMemory>& eventMemory)
-{
- Mutex::Autolock _l(mLock);
- if (eventMemory == 0 || eventMemory->unsecurePointer() == NULL) {
- return;
- }
-
- if (mCallback != 0) {
- // Memory is coming from a trusted process.
- mCallback->onSoundModelEvent(
- (struct sound_trigger_model_event *)eventMemory->unsecurePointer());
- }
-}
-
-void SoundTrigger::onServiceStateChange(const sp<IMemory>& eventMemory)
-{
- Mutex::Autolock _l(mLock);
- if (eventMemory == 0 || eventMemory->unsecurePointer() == NULL) {
- return;
- }
-
- if (mCallback != 0) {
- // Memory is coming from a trusted process.
- mCallback->onServiceStateChange(
- *((sound_trigger_service_state_t *)eventMemory->unsecurePointer()));
- }
-}
-
-//IBinder::DeathRecipient
-void SoundTrigger::binderDied(const wp<IBinder>& who __unused) {
- Mutex::Autolock _l(mLock);
- ALOGW("SoundTrigger server binder Died ");
- mISoundTrigger = 0;
- if (mCallback != 0) {
- mCallback->onServiceDied();
- }
-}
-
-status_t SoundTrigger::stringToGuid(const char *str, sound_trigger_uuid_t *guid)
-{
- if (str == NULL || guid == NULL) {
- return BAD_VALUE;
- }
-
- int tmp[10];
-
- if (sscanf(str, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
- tmp, tmp+1, tmp+2, tmp+3, tmp+4, tmp+5, tmp+6, tmp+7, tmp+8, tmp+9) < 10) {
- return BAD_VALUE;
- }
- guid->timeLow = (uint32_t)tmp[0];
- guid->timeMid = (uint16_t)tmp[1];
- guid->timeHiAndVersion = (uint16_t)tmp[2];
- guid->clockSeq = (uint16_t)tmp[3];
- guid->node[0] = (uint8_t)tmp[4];
- guid->node[1] = (uint8_t)tmp[5];
- guid->node[2] = (uint8_t)tmp[6];
- guid->node[3] = (uint8_t)tmp[7];
- guid->node[4] = (uint8_t)tmp[8];
- guid->node[5] = (uint8_t)tmp[9];
-
- return NO_ERROR;
-}
-
-status_t SoundTrigger::guidToString(const sound_trigger_uuid_t *guid, char *str, size_t maxLen)
-{
- if (guid == NULL || str == NULL) {
- return BAD_VALUE;
- }
-
- snprintf(str, maxLen, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
- guid->timeLow,
- guid->timeMid,
- guid->timeHiAndVersion,
- guid->clockSeq,
- guid->node[0],
- guid->node[1],
- guid->node[2],
- guid->node[3],
- guid->node[4],
- guid->node[5]);
-
- return NO_ERROR;
-}
-
-}; // namespace android