Merge "stagefright: move to system_ext" am: 9fe6ffa891 am: caf501872d am: 9d6fca51de
Change-Id: Idaae8003f74c342dfafbabfe899aa7eb3071074a
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
new file mode 100644
index 0000000..e63185d
--- /dev/null
+++ b/PREUPLOAD.cfg
@@ -0,0 +1,2 @@
+[Hook Scripts]
+mainline_hook = tools/mainline_hook.sh ${PREUPLOAD_COMMIT} "."
diff --git a/apex/ld.config.txt b/apex/ld.config.txt
index bd6af83..713f0b7 100644
--- a/apex/ld.config.txt
+++ b/apex/ld.config.txt
@@ -33,7 +33,7 @@
# TODO: replace the following when apex has a way to auto-generate this list
# namespace.default.link.platform.shared_libs = %LLNDK_LIBRARIES%
# namespace.default.link.platform.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
-namespace.default.link.platform.shared_libs = libEGL.so:libGLESv1_CM.so:libGLESv2.so:libGLESv3.so:libRS.so:libandroid_net.so:libc.so:libcgrouprc.so:libclang_rt.asan-aarch64-android.so:libclang_rt.asan-arm-android.so:libclang_rt.hwasan-aarch64-android.so:libclang_rt.asan-i686-android.so:libclang_rt.asan-x86_64-android.so:libdl.so:libft2.so:liblog.so:libm.so:libmediandk.so:libnativewindow.so:libneuralnetworks.so:libsync.so:libvndksupport.so:libdl_android.so:libvulkan.so
+namespace.default.link.platform.shared_libs = libEGL.so:libGLESv1_CM.so:libGLESv2.so:libGLESv3.so:libRS.so:libandroid_net.so:libc.so:libcgrouprc.so:libclang_rt.asan-aarch64-android.so:libclang_rt.asan-arm-android.so:libclang_rt.hwasan-aarch64-android.so:libclang_rt.asan-i686-android.so:libclang_rt.asan-x86_64-android.so:libdl.so:libft2.so:liblog.so:libm.so:libmediandk.so:libnativewindow.so:libneuralnetworks.so:libsync.so:libvndksupport.so:libdl_android.so:libvulkan.so:libbinder_ndk.so
###############################################################################
# "platform" namespace
@@ -44,9 +44,14 @@
namespace.platform.isolated = true
namespace.platform.search.paths = /system/${LIB}
-namespace.platform.search.paths += /apex/com.android.runtime/${LIB}
namespace.platform.asan.search.paths = /data/asan/system/${LIB}
namespace.platform.asan.search.paths += /system/${LIB}
+
+# TODO(b/140790209): These directories are wrong in R and later because they
+# only contain Bionic internal libraries dependencies that should not be
+# accessed from the outside. However, they may be necessary for APEX builds that
+# are pushed to Q. Remove them as soon as Q compatibility is no longer required.
+namespace.platform.search.paths += /apex/com.android.runtime/${LIB}
namespace.platform.asan.search.paths += /apex/com.android.runtime/${LIB}
# /system/lib/libc.so, etc are symlinks to /apex/com.android.lib/lib/bionic/libc.so, etc.
@@ -133,7 +138,7 @@
# TODO: replace the following when apex has a way to auto-generate this list
# namespace.sphal.link.platform.shared_libs = %LLNDK_LIBRARIES%
# namespace.sphal.link.platform.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
-namespace.sphal.link.platform.shared_libs = libEGL.so:libGLESv1_CM.so:libGLESv2.so:libGLESv3.so:libRS.so:libandroid_net.so:libc.so:libcgrouprc.so:libclang_rt.asan-aarch64-android.so:libclang_rt.asan-arm-android.so:libclang_rt.hwasan-aarch64-android.so:libclang_rt.asan-i686-android.so:libclang_rt.asan-x86_64-android.so:libdl.so:libft2.so:liblog.so:libm.so:libmediandk.so:libnativewindow.so:libneuralnetworks.so:libsync.so:libvndksupport.so:libvulkan.so
+namespace.sphal.link.platform.shared_libs = libEGL.so:libGLESv1_CM.so:libGLESv2.so:libGLESv3.so:libRS.so:libandroid_net.so:libc.so:libcgrouprc.so:libclang_rt.asan-aarch64-android.so:libclang_rt.asan-arm-android.so:libclang_rt.hwasan-aarch64-android.so:libclang_rt.asan-i686-android.so:libclang_rt.asan-x86_64-android.so:libdl.so:libft2.so:liblog.so:libm.so:libmediandk.so:libnativewindow.so:libneuralnetworks.so:libsync.so:libvndksupport.so:libvulkan.so:libbinder_ndk.so
# Add a link for libz.so which is llndk on devices where VNDK is not enforced.
namespace.sphal.link.platform.shared_libs += libz.so
diff --git a/apex/manifest.json b/apex/manifest.json
index 3011ee8..ddd642e 100644
--- a/apex/manifest.json
+++ b/apex/manifest.json
@@ -1,4 +1,4 @@
{
"name": "com.android.media",
- "version": 290000000
+ "version": 300000000
}
diff --git a/apex/manifest_codec.json b/apex/manifest_codec.json
index 83a5178..2320fd7 100644
--- a/apex/manifest_codec.json
+++ b/apex/manifest_codec.json
@@ -1,4 +1,4 @@
{
"name": "com.android.media.swcodec",
- "version": 290000000
+ "version": 300000000
}
diff --git a/camera/Android.bp b/camera/Android.bp
index b288bcf..ea7259a 100644
--- a/camera/Android.bp
+++ b/camera/Android.bp
@@ -85,6 +85,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/Camera.cpp b/camera/Camera.cpp
index c6c35ef..84d1d93 100644
--- a/camera/Camera.cpp
+++ b/camera/Camera.cpp
@@ -347,6 +347,20 @@
return c->setPreviewCallbackTarget(callbackProducer);
}
+status_t Camera::setAudioRestriction(int32_t mode)
+{
+ sp <::android::hardware::ICamera> c = mCamera;
+ if (c == 0) return NO_INIT;
+ return c->setAudioRestriction(mode);
+}
+
+int32_t Camera::getGlobalAudioRestriction()
+{
+ sp <::android::hardware::ICamera> c = mCamera;
+ if (c == 0) return NO_INIT;
+ return c->getGlobalAudioRestriction();
+}
+
// callback from camera service
void Camera::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2)
{
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/ICamera.cpp b/camera/ICamera.cpp
index f0945c7..b83edf7 100644
--- a/camera/ICamera.cpp
+++ b/camera/ICamera.cpp
@@ -56,6 +56,8 @@
SET_VIDEO_BUFFER_TARGET,
RELEASE_RECORDING_FRAME_HANDLE,
RELEASE_RECORDING_FRAME_HANDLE_BATCH,
+ SET_AUDIO_RESTRICTION,
+ GET_GLOBAL_AUDIO_RESTRICTION,
};
class BpCamera: public BpInterface<ICamera>
@@ -191,6 +193,21 @@
}
}
+ status_t setAudioRestriction(int32_t mode) {
+ Parcel data, reply;
+ data.writeInterfaceToken(ICamera::getInterfaceDescriptor());
+ data.writeInt32(mode);
+ remote()->transact(SET_AUDIO_RESTRICTION, data, &reply);
+ return reply.readInt32();
+ }
+
+ int32_t getGlobalAudioRestriction() {
+ Parcel data, reply;
+ data.writeInterfaceToken(ICamera::getInterfaceDescriptor());
+ remote()->transact(GET_GLOBAL_AUDIO_RESTRICTION, data, &reply);
+ return reply.readInt32();
+ }
+
status_t setVideoBufferMode(int32_t videoBufferMode)
{
ALOGV("setVideoBufferMode: %d", videoBufferMode);
@@ -494,6 +511,17 @@
reply->writeInt32(setVideoTarget(st));
return NO_ERROR;
} break;
+ case SET_AUDIO_RESTRICTION: {
+ CHECK_INTERFACE(ICamera, data, reply);
+ int32_t mode = data.readInt32();
+ reply->writeInt32(setAudioRestriction(mode));
+ return NO_ERROR;
+ } break;
+ case GET_GLOBAL_AUDIO_RESTRICTION: {
+ CHECK_INTERFACE(ICamera, data, reply);
+ reply->writeInt32(getGlobalAudioRestriction());
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/camera/aidl/android/hardware/ICameraService.aidl b/camera/aidl/android/hardware/ICameraService.aidl
index 3e8992a..62dbb5e 100644
--- a/camera/aidl/android/hardware/ICameraService.aidl
+++ b/camera/aidl/android/hardware/ICameraService.aidl
@@ -87,6 +87,7 @@
ICameraDeviceUser connectDevice(ICameraDeviceCallbacks callbacks,
String cameraId,
String opPackageName,
+ @nullable String featureId,
int clientUid);
/**
diff --git a/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl b/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
index 49dfde8..cdb7ac3 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;
@@ -155,4 +157,37 @@
void updateOutputConfiguration(int streamId, in OutputConfiguration outputConfiguration);
void finalizeOutputConfigurations(int streamId, in OutputConfiguration outputConfiguration);
+
+
+ // Keep in sync with public API in
+ // frameworks/base/core/java/android/hardware/camera2/CameraDevice.java
+ const int AUDIO_RESTRICTION_NONE = 0;
+ const int AUDIO_RESTRICTION_VIBRATION = 1;
+ const int AUDIO_RESTRICTION_VIBRATION_SOUND = 3;
+
+ /**
+ * Set audio restriction mode for this camera device.
+ *
+ * @param mode the audio restriction mode ID as above
+ *
+ */
+ void setCameraAudioRestriction(int mode);
+
+ /**
+ * Get global audio restriction mode for all camera clients.
+ *
+ * @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 Surface[] offlineOutputs);
}
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/include/camera/Camera.h b/camera/include/camera/Camera.h
index 430aa1c..2cdb617 100644
--- a/camera/include/camera/Camera.h
+++ b/camera/include/camera/Camera.h
@@ -167,6 +167,9 @@
sp<ICameraRecordingProxy> getRecordingProxy();
+ status_t setAudioRestriction(int32_t mode);
+ int32_t getGlobalAudioRestriction();
+
// ICameraClient interface
virtual void notifyCallback(int32_t msgType, int32_t ext, int32_t ext2);
virtual void dataCallback(int32_t msgType, const sp<IMemory>& dataPtr,
diff --git a/camera/include/camera/android/hardware/ICamera.h b/camera/include/camera/android/hardware/ICamera.h
index 80823d6..ec19e5d 100644
--- a/camera/include/camera/android/hardware/ICamera.h
+++ b/camera/include/camera/android/hardware/ICamera.h
@@ -140,6 +140,12 @@
// Set the video buffer producer for camera to use in VIDEO_BUFFER_MODE_BUFFER_QUEUE mode.
virtual status_t setVideoTarget(
const sp<IGraphicBufferProducer>& bufferProducer) = 0;
+
+ // Set the audio restriction mode
+ virtual status_t setAudioRestriction(int32_t mode) = 0;
+
+ // Get the global audio restriction mode
+ virtual int32_t getGlobalAudioRestriction() = 0;
};
// ----------------------------------------------------------------------------
diff --git a/camera/ndk/impl/ACameraManager.cpp b/camera/ndk/impl/ACameraManager.cpp
index 457dea9..7a0f63b 100644
--- a/camera/ndk/impl/ACameraManager.cpp
+++ b/camera/ndk/impl/ACameraManager.cpp
@@ -540,7 +540,7 @@
// No way to get package name from native.
// Send a zero length package name and let camera service figure it out from UID
binder::Status serviceRet = cs->connectDevice(
- callbacks, String16(cameraId), String16(""),
+ callbacks, String16(cameraId), String16(""), std::unique_ptr<String16>(),
hardware::ICameraService::USE_CALLING_UID, /*out*/&deviceRemote);
if (!serviceRet.isOk()) {
diff --git a/camera/ndk/impl/ACameraMetadata.cpp b/camera/ndk/impl/ACameraMetadata.cpp
index 77dcd48..e15e1a0 100644
--- a/camera/ndk/impl/ACameraMetadata.cpp
+++ b/camera/ndk/impl/ACameraMetadata.cpp
@@ -479,6 +479,8 @@
case ACAMERA_CONTROL_VIDEO_STABILIZATION_MODE:
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:
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index 8dd6e00..49783db 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -140,7 +140,7 @@
* application controls how the color mapping is performed.</p>
* <p>We define the expected processing pipeline below. For consistency
* across devices, this is always the case with TRANSFORM_MATRIX.</p>
- * <p>When either FULL or HIGH_QUALITY is used, the camera device may
+ * <p>When either FAST or HIGH_QUALITY is used, the camera device may
* do additional processing but ACAMERA_COLOR_CORRECTION_GAINS and
* ACAMERA_COLOR_CORRECTION_TRANSFORM will still be provided by the
* camera device (in the results) and be roughly correct.</p>
@@ -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
@@ -1126,10 +1159,17 @@
* </ul>
* <p>For devices at the LIMITED level or above:</p>
* <ul>
- * <li>For YUV_420_888 burst capture use case, this list will always include (<code>min</code>, <code>max</code>)
- * and (<code>max</code>, <code>max</code>) where <code>min</code> <= 15 and <code>max</code> = the maximum output frame rate of the
+ * <li>For devices that advertise NIR color filter arrangement in
+ * ACAMERA_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT, this list will always include
+ * (<code>max</code>, <code>max</code>) where <code>max</code> = the maximum output frame rate of the maximum YUV_420_888
+ * output size.</li>
+ * <li>For devices advertising any color filter arrangement other than NIR, or devices not
+ * advertising color filter arrangement, this list will always include (<code>min</code>, <code>max</code>) and
+ * (<code>max</code>, <code>max</code>) where <code>min</code> <= 15 and <code>max</code> = the maximum output frame rate of the
* maximum YUV_420_888 output size.</li>
* </ul>
+ *
+ * @see ACAMERA_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
*/
ACAMERA_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES = // int32[2*n]
ACAMERA_CONTROL_START + 20,
@@ -1304,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>
*
@@ -1727,6 +1767,169 @@
*/
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 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>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>For OFF mode, the camera behaves normally with no bokeh effect.</p>
+ * <p>For STILL_CAPTURE mode, the maximum streaming dimension specifies the limit under which
+ * bokeh is effective when capture intent is PREVIEW. Note that when capture intent is
+ * PREVIEW, the bokeh effect may not be as high quality compared to STILL_CAPTURE intent
+ * in order to maintain reasonable frame rate. The maximum streaming dimension must be one
+ * of the YUV_420_888 or PRIVATE resolutions in availableStreamConfigurations, or (0, 0)
+ * if preview bokeh is not supported. If the application configures a stream larger than
+ * the maximum streaming dimension, bokeh effect may not be applied for this stream for
+ * PREVIEW intent.</p>
+ * <p>For CONTINUOUS mode, the maximum streaming dimension specifies the limit under which
+ * bokeh is effective. This dimension must be one of the YUV_420_888 or PRIVATE resolutions
+ * in availableStreamConfigurations, and if the sensor maximum resolution is larger than or
+ * equal to 1080p, the maximum streaming dimension must be at least 1080p. If the
+ * application configures a stream with larger dimension, the stream may not have bokeh
+ * effect applied.</p>
+ */
+ 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>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
+ * <li>ACaptureRequest</li>
+ * </ul></p>
+ *
+ * <p>With bokeh mode, the camera device may blur out the parts of scene that are not in
+ * focus, creating a bokeh (or shallow depth of field) effect for people or objects.</p>
+ * <p>When set to STILL_CAPTURE bokeh mode with STILL_CAPTURE capture intent, due to the extra
+ * processing needed for high quality bokeh effect, the stall may be longer than when
+ * capture intent is not STILL_CAPTURE.</p>
+ * <p>When set to STILL_CAPTURE bokeh mode with PREVIEW capture intent,</p>
+ * <ul>
+ * <li>If the camera device has BURST_CAPTURE capability, the frame rate requirement of
+ * BURST_CAPTURE must still be met.</li>
+ * <li>All streams not larger than the maximum streaming dimension for STILL_CAPTURE mode
+ * (queried via {@link ACAMERA_CONTROL_AVAILABLE_BOKEH_CAPABILITIES })
+ * will have preview bokeh effect applied.</li>
+ * </ul>
+ * <p>When set to CONTINUOUS mode, configured streams dimension should not exceed this mode's
+ * maximum streaming dimension in order to have bokeh effect applied. Bokeh effect may not
+ * be available for streams larger than the maximum streaming dimension.</p>
+ * <p>Switching between different bokeh modes may involve reconfiguration of the camera
+ * pipeline, resulting in long latency. The application should check this key against the
+ * available session keys queried via
+ * {@link ACameraManager_getCameraCharacteristics }.</p>
+ * <p>When bokeh mode is on, the camera device may override certain control parameters, such as
+ * reduce frame rate or use face priority scene mode, to achieve best power and quality
+ * tradeoffs. When turned on, AE, AWB, and AF run in auto modes, and only the mandatory
+ * 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 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 + 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,
/**
@@ -3085,32 +3288,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
@@ -3121,14 +3362,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
*/
@@ -3154,6 +3403,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,
@@ -3328,8 +3583,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
*/
@@ -3536,11 +3807,19 @@
* output capture result.</p>
* <p>This control is only effective if ACAMERA_CONTROL_AE_MODE or ACAMERA_CONTROL_MODE is set to
* OFF; otherwise the auto-exposure algorithm will override this value.</p>
+ * <p>Note that for devices supporting postRawSensitivityBoost, the total sensitivity applied
+ * to the final processed image is the combination of ACAMERA_SENSOR_SENSITIVITY and
+ * ACAMERA_CONTROL_POST_RAW_SENSITIVITY_BOOST. In case the application uses the sensor
+ * sensitivity from last capture result of an auto request for a manual request, in order
+ * to achieve the same brightness in the output image, the application should also
+ * set postRawSensitivityBoost.</p>
*
* @see ACAMERA_CONTROL_AE_MODE
* @see ACAMERA_CONTROL_MODE
+ * @see ACAMERA_CONTROL_POST_RAW_SENSITIVITY_BOOST
* @see ACAMERA_SENSOR_INFO_SENSITIVITY_RANGE
* @see ACAMERA_SENSOR_MAX_ANALOG_SENSITIVITY
+ * @see ACAMERA_SENSOR_SENSITIVITY
*/
ACAMERA_SENSOR_SENSITIVITY = // int32
ACAMERA_SENSOR_START + 2,
@@ -4621,9 +4900,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
@@ -4652,10 +4942,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
@@ -6987,6 +7288,31 @@
} acamera_metadata_enum_android_control_af_scene_change_t;
+// ACAMERA_CONTROL_BOKEH_MODE
+typedef enum acamera_metadata_enum_acamera_control_bokeh_mode {
+ /**
+ * <p>Bokeh mode is disabled.</p>
+ */
+ ACAMERA_CONTROL_BOKEH_MODE_OFF = 0,
+
+ /**
+ * <p>High quality bokeh mode is enabled for all non-raw streams (including YUV,
+ * JPEG, and IMPLEMENTATION_DEFINED) when capture intent is STILL_CAPTURE. Due to the
+ * extra image processing, this mode may introduce additional stall to non-raw streams.
+ * This mode should be used in high quality still capture use case.</p>
+ */
+ ACAMERA_CONTROL_BOKEH_MODE_STILL_CAPTURE = 1,
+
+ /**
+ * <p>Bokeh effect must not slow down capture rate relative to sensor raw output,
+ * and the effect is applied to all processed streams no larger than the maximum
+ * streaming dimension. This mode should be used if performance and power are a
+ * priority, such as video recording.</p>
+ */
+ ACAMERA_CONTROL_BOKEH_MODE_CONTINUOUS = 2,
+
+} acamera_metadata_enum_android_control_bokeh_mode_t;
+
// ACAMERA_EDGE_MODE
@@ -7751,6 +8077,13 @@
*/
ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA = 13,
+ /**
+ * <p>The camera device is only accessible by Android's system components and privileged
+ * applications. Processes need to have the android.permission.SYSTEM_CAMERA in
+ * addition to android.permission.CAMERA in order to connect to this camera device.</p>
+ */
+ ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA = 14,
+
} acamera_metadata_enum_android_request_available_capabilities_t;
diff --git a/camera/tests/CameraBinderTests.cpp b/camera/tests/CameraBinderTests.cpp
index 8fe029a..9a18b10 100644
--- a/camera/tests/CameraBinderTests.cpp
+++ b/camera/tests/CameraBinderTests.cpp
@@ -57,7 +57,7 @@
#include <algorithm>
using namespace android;
-using ::android::hardware::ICameraServiceDefault;
+using ::android::hardware::ICameraService;
using ::android::hardware::camera2::ICameraDeviceUser;
#define ASSERT_NOT_NULL(x) \
@@ -361,7 +361,8 @@
sp<TestCameraDeviceCallbacks> callbacks(new TestCameraDeviceCallbacks());
sp<hardware::camera2::ICameraDeviceUser> device;
res = service->connectDevice(callbacks, cameraId, String16("meeeeeeeee!"),
- hardware::ICameraService::USE_CALLING_UID, /*out*/&device);
+ std::unique_ptr<String16>(), hardware::ICameraService::USE_CALLING_UID,
+ /*out*/&device);
EXPECT_TRUE(res.isOk()) << res;
ASSERT_NE(nullptr, device.get());
device->disconnect();
@@ -403,7 +404,8 @@
{
SCOPED_TRACE("openNewDevice");
binder::Status res = service->connectDevice(callbacks, deviceId, String16("meeeeeeeee!"),
- hardware::ICameraService::USE_CALLING_UID, /*out*/&device);
+ std::unique_ptr<String16>(), hardware::ICameraService::USE_CALLING_UID,
+ /*out*/&device);
EXPECT_TRUE(res.isOk()) << res;
}
auto p = std::make_pair(callbacks, device);
@@ -507,7 +509,7 @@
bool queryStatus;
res = device->isSessionConfigurationSupported(sessionConfiguration, &queryStatus);
EXPECT_TRUE(res.isOk() ||
- (res.serviceSpecificErrorCode() == ICameraServiceDefault::ERROR_INVALID_OPERATION))
+ (res.serviceSpecificErrorCode() == ICameraService::ERROR_INVALID_OPERATION))
<< res;
if (res.isOk()) {
EXPECT_TRUE(queryStatus);
diff --git a/cmds/screenrecord/Android.bp b/cmds/screenrecord/Android.bp
index 6bdbab1..72008c2 100644
--- a/cmds/screenrecord/Android.bp
+++ b/cmds/screenrecord/Android.bp
@@ -31,6 +31,7 @@
shared_libs: [
"libstagefright",
"libmedia",
+ "libmediandk",
"libmedia_omx",
"libutils",
"libbinder",
diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index f2a71b3..b534f8a 100644
--- a/cmds/screenrecord/screenrecord.cpp
+++ b/cmds/screenrecord/screenrecord.cpp
@@ -37,6 +37,7 @@
#include <binder/IPCThreadState.h>
#include <utils/Errors.h>
+#include <utils/SystemClock.h>
#include <utils/Timers.h>
#include <utils/Trace.h>
@@ -44,13 +45,15 @@
#include <gui/SurfaceComposerClient.h>
#include <gui/ISurfaceComposer.h>
#include <ui/DisplayInfo.h>
+#include <media/NdkMediaCodec.h>
+#include <media/NdkMediaFormatPriv.h>
+#include <media/NdkMediaMuxer.h>
#include <media/openmax/OMX_IVCommon.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/MediaCodecConstants.h>
#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MediaMuxer.h>
#include <media/stagefright/PersistentSurface.h>
#include <mediadrm/ICrypto.h>
#include <media/MediaCodecBuffer.h>
@@ -70,9 +73,9 @@
using android::ISurfaceComposer;
using android::MediaCodec;
using android::MediaCodecBuffer;
-using android::MediaMuxer;
using android::Overlay;
using android::PersistentSurface;
+using android::PhysicalDisplayId;
using android::ProcessState;
using android::Rect;
using android::String8;
@@ -95,6 +98,8 @@
static const uint32_t kFallbackWidth = 1280; // 720p
static const uint32_t kFallbackHeight = 720;
static const char* kMimeTypeAvc = "video/avc";
+static const char* kMimeTypeApplicationOctetstream = "application/octet-stream";
+static const char* kWinscopeMagicString = "#VV1NSC0PET1ME!#";
// Command-line parameters.
static bool gVerbose = false; // chatty on stdout
@@ -113,7 +118,7 @@
static uint32_t gBitRate = 20000000; // 20Mbps
static uint32_t gTimeLimitSec = kMaxTimeLimitSec;
static uint32_t gBframes = 0;
-
+static PhysicalDisplayId gPhysicalDisplayId;
// Set by signal handler to stop recording.
static volatile bool gStopRequested = false;
@@ -266,14 +271,14 @@
static status_t setDisplayProjection(
SurfaceComposerClient::Transaction& t,
const sp<IBinder>& dpy,
- const DisplayInfo& mainDpyInfo) {
+ const DisplayInfo& displayInfo) {
// Set the region of the layer stack we're interested in, which in our
// case is "all of it".
- Rect layerStackRect(mainDpyInfo.viewportW, mainDpyInfo.viewportH);
+ Rect layerStackRect(displayInfo.viewportW, displayInfo.viewportH);
// We need to preserve the aspect ratio of the display.
- float displayAspect = (float) mainDpyInfo.viewportH / (float) mainDpyInfo.viewportW;
+ float displayAspect = (float) displayInfo.viewportH / (float) displayInfo.viewportW;
// Set the way we map the output onto the display surface (which will
@@ -332,16 +337,15 @@
* Configures the virtual display. When this completes, virtual display
* frames will start arriving from the buffer producer.
*/
-static status_t prepareVirtualDisplay(const DisplayInfo& mainDpyInfo,
+static status_t prepareVirtualDisplay(const DisplayInfo& displayInfo,
const sp<IGraphicBufferProducer>& bufferProducer,
sp<IBinder>* pDisplayHandle) {
sp<IBinder> dpy = SurfaceComposerClient::createDisplay(
String8("ScreenRecorder"), false /*secure*/);
-
SurfaceComposerClient::Transaction t;
t.setDisplaySurface(dpy, bufferProducer);
- setDisplayProjection(t, dpy, mainDpyInfo);
- t.setDisplayLayerStack(dpy, 0); // default stack
+ setDisplayProjection(t, dpy, displayInfo);
+ t.setDisplayLayerStack(dpy, displayInfo.layerStack);
t.apply();
*pDisplayHandle = dpy;
@@ -350,6 +354,56 @@
}
/*
+ * Writes an unsigned integer byte-by-byte in little endian order regardless
+ * of the platform endianness.
+ */
+template <typename UINT>
+static void writeValueLE(UINT value, uint8_t* buffer) {
+ for (int i = 0; i < sizeof(UINT); ++i) {
+ buffer[i] = static_cast<uint8_t>(value);
+ value >>= 8;
+ }
+}
+
+/*
+ * Saves frames presentation time relative to the elapsed realtime clock in microseconds
+ * preceded by a Winscope magic string and frame count to a metadata track.
+ * This metadata is used by the Winscope tool to sync video with SurfaceFlinger
+ * and WindowManager traces.
+ *
+ * The metadata is written as a binary array as follows:
+ * - winscope magic string (kWinscopeMagicString constant), without trailing null char,
+ * - the number of recorded frames (as little endian uint32),
+ * - for every frame its presentation time relative to the elapsed realtime clock in microseconds
+ * (as little endian uint64).
+ */
+static status_t writeWinscopeMetadata(const Vector<int64_t>& timestamps,
+ const ssize_t metaTrackIdx, AMediaMuxer *muxer) {
+ ALOGV("Writing metadata");
+ int64_t systemTimeToElapsedTimeOffsetMicros = (android::elapsedRealtimeNano()
+ - systemTime(SYSTEM_TIME_MONOTONIC)) / 1000;
+ sp<ABuffer> buffer = new ABuffer(timestamps.size() * sizeof(int64_t)
+ + sizeof(uint32_t) + strlen(kWinscopeMagicString));
+ uint8_t* pos = buffer->data();
+ strcpy(reinterpret_cast<char*>(pos), kWinscopeMagicString);
+ pos += strlen(kWinscopeMagicString);
+ writeValueLE<uint32_t>(timestamps.size(), pos);
+ pos += sizeof(uint32_t);
+ for (size_t idx = 0; idx < timestamps.size(); ++idx) {
+ writeValueLE<uint64_t>(static_cast<uint64_t>(timestamps[idx]
+ + systemTimeToElapsedTimeOffsetMicros), pos);
+ pos += sizeof(uint64_t);
+ }
+ AMediaCodecBufferInfo bufferInfo = {
+ 0,
+ static_cast<int32_t>(buffer->size()),
+ timestamps[0],
+ 0
+ };
+ return AMediaMuxer_writeSampleData(muxer, metaTrackIdx, buffer->data(), &bufferInfo);
+}
+
+/*
* Runs the MediaCodec encoder, sending the output to the MediaMuxer. The
* input frames are coming from the virtual display as fast as SurfaceFlinger
* wants to send them.
@@ -359,15 +413,17 @@
* The muxer must *not* have been started before calling.
*/
static status_t runEncoder(const sp<MediaCodec>& encoder,
- const sp<MediaMuxer>& muxer, FILE* rawFp, const sp<IBinder>& mainDpy,
+ AMediaMuxer *muxer, FILE* rawFp, const sp<IBinder>& display,
const sp<IBinder>& virtualDpy, uint8_t orientation) {
static int kTimeout = 250000; // be responsive on signal
status_t err;
ssize_t trackIdx = -1;
+ ssize_t metaTrackIdx = -1;
uint32_t debugNumFrames = 0;
int64_t startWhenNsec = systemTime(CLOCK_MONOTONIC);
int64_t endWhenNsec = startWhenNsec + seconds_to_nanoseconds(gTimeLimitSec);
- DisplayInfo mainDpyInfo;
+ DisplayInfo displayInfo;
+ Vector<int64_t> timestamps;
bool firstFrame = true;
assert((rawFp == NULL && muxer != NULL) || (rawFp != NULL && muxer == NULL));
@@ -423,16 +479,16 @@
//
// Polling for changes is inefficient and wrong, but the
// useful stuff is hard to get at without a Dalvik VM.
- err = SurfaceComposerClient::getDisplayInfo(mainDpy,
- &mainDpyInfo);
+ err = SurfaceComposerClient::getDisplayInfo(display,
+ &displayInfo);
if (err != NO_ERROR) {
ALOGW("getDisplayInfo(main) failed: %d", err);
- } else if (orientation != mainDpyInfo.orientation) {
- ALOGD("orientation changed, now %d", mainDpyInfo.orientation);
+ } else if (orientation != displayInfo.orientation) {
+ ALOGD("orientation changed, now %d", displayInfo.orientation);
SurfaceComposerClient::Transaction t;
- setDisplayProjection(t, virtualDpy, mainDpyInfo);
+ setDisplayProjection(t, virtualDpy, displayInfo);
t.apply();
- orientation = mainDpyInfo.orientation;
+ orientation = displayInfo.orientation;
}
}
@@ -464,13 +520,21 @@
// TODO
sp<ABuffer> buffer = new ABuffer(
buffers[bufIndex]->data(), buffers[bufIndex]->size());
- err = muxer->writeSampleData(buffer, trackIdx,
- ptsUsec, flags);
+ AMediaCodecBufferInfo bufferInfo = {
+ 0,
+ static_cast<int32_t>(buffer->size()),
+ ptsUsec,
+ flags
+ };
+ err = AMediaMuxer_writeSampleData(muxer, trackIdx, buffer->data(), &bufferInfo);
if (err != NO_ERROR) {
fprintf(stderr,
"Failed writing data to muxer (err=%d)\n", err);
return err;
}
+ if (gOutputFormat == FORMAT_MP4) {
+ timestamps.add(ptsUsec);
+ }
}
debugNumFrames++;
}
@@ -495,10 +559,18 @@
ALOGV("Encoder format changed");
sp<AMessage> newFormat;
encoder->getOutputFormat(&newFormat);
+ // TODO remove when MediaCodec has been replaced with AMediaCodec
+ AMediaFormat *ndkFormat = AMediaFormat_fromMsg(&newFormat);
if (muxer != NULL) {
- trackIdx = muxer->addTrack(newFormat);
+ trackIdx = AMediaMuxer_addTrack(muxer, ndkFormat);
+ if (gOutputFormat == FORMAT_MP4) {
+ AMediaFormat *metaFormat = AMediaFormat_new();
+ AMediaFormat_setString(metaFormat, AMEDIAFORMAT_KEY_MIME, kMimeTypeApplicationOctetstream);
+ metaTrackIdx = AMediaMuxer_addTrack(muxer, metaFormat);
+ AMediaFormat_delete(metaFormat);
+ }
ALOGV("Starting muxer");
- err = muxer->start();
+ err = AMediaMuxer_start(muxer);
if (err != NO_ERROR) {
fprintf(stderr, "Unable to start muxer (err=%d)\n", err);
return err;
@@ -533,6 +605,13 @@
systemTime(CLOCK_MONOTONIC) - startWhenNsec));
fflush(stdout);
}
+ if (metaTrackIdx >= 0 && !timestamps.isEmpty()) {
+ err = writeWinscopeMetadata(timestamps, metaTrackIdx, muxer);
+ if (err != NO_ERROR) {
+ fprintf(stderr, "Failed writing metadata to muxer (err=%d)\n", err);
+ return err;
+ }
+ }
return NO_ERROR;
}
@@ -597,32 +676,33 @@
self->startThreadPool();
// Get main display parameters.
- const sp<IBinder> mainDpy = SurfaceComposerClient::getInternalDisplayToken();
- if (mainDpy == nullptr) {
+ sp<IBinder> display = SurfaceComposerClient::getPhysicalDisplayToken(
+ gPhysicalDisplayId);
+ if (display == nullptr) {
fprintf(stderr, "ERROR: no display\n");
return NAME_NOT_FOUND;
}
- DisplayInfo mainDpyInfo;
- err = SurfaceComposerClient::getDisplayInfo(mainDpy, &mainDpyInfo);
+ DisplayInfo displayInfo;
+ err = SurfaceComposerClient::getDisplayInfo(display, &displayInfo);
if (err != NO_ERROR) {
fprintf(stderr, "ERROR: unable to get display characteristics\n");
return err;
}
if (gVerbose) {
- printf("Main display is %dx%d @%.2ffps (orientation=%u)\n",
- mainDpyInfo.viewportW, mainDpyInfo.viewportH, mainDpyInfo.fps,
- mainDpyInfo.orientation);
+ printf("Display is %dx%d @%.2ffps (orientation=%u), layerStack=%u\n",
+ displayInfo.viewportW, displayInfo.viewportH, displayInfo.fps,
+ displayInfo.orientation, displayInfo.layerStack);
fflush(stdout);
}
// Encoder can't take odd number as config
if (gVideoWidth == 0) {
- gVideoWidth = floorToEven(mainDpyInfo.viewportW);
+ gVideoWidth = floorToEven(displayInfo.viewportW);
}
if (gVideoHeight == 0) {
- gVideoHeight = floorToEven(mainDpyInfo.viewportH);
+ gVideoHeight = floorToEven(displayInfo.viewportH);
}
// Configure and start the encoder.
@@ -630,7 +710,7 @@
sp<FrameOutput> frameOutput;
sp<IGraphicBufferProducer> encoderInputSurface;
if (gOutputFormat != FORMAT_FRAMES && gOutputFormat != FORMAT_RAW_FRAMES) {
- err = prepareEncoder(mainDpyInfo.fps, &encoder, &encoderInputSurface);
+ err = prepareEncoder(displayInfo.fps, &encoder, &encoderInputSurface);
if (err != NO_ERROR && !gSizeSpecified) {
// fallback is defined for landscape; swap if we're in portrait
@@ -643,7 +723,7 @@
gVideoWidth, gVideoHeight, newWidth, newHeight);
gVideoWidth = newWidth;
gVideoHeight = newHeight;
- err = prepareEncoder(mainDpyInfo.fps, &encoder,
+ err = prepareEncoder(displayInfo.fps, &encoder,
&encoderInputSurface);
}
}
@@ -691,13 +771,13 @@
// Configure virtual display.
sp<IBinder> dpy;
- err = prepareVirtualDisplay(mainDpyInfo, bufferProducer, &dpy);
+ err = prepareVirtualDisplay(displayInfo, bufferProducer, &dpy);
if (err != NO_ERROR) {
if (encoder != NULL) encoder->release();
return err;
}
- sp<MediaMuxer> muxer = NULL;
+ AMediaMuxer *muxer = nullptr;
FILE* rawFp = NULL;
switch (gOutputFormat) {
case FORMAT_MP4:
@@ -716,15 +796,15 @@
abort();
}
if (gOutputFormat == FORMAT_MP4) {
- muxer = new MediaMuxer(fd, MediaMuxer::OUTPUT_FORMAT_MPEG_4);
+ muxer = AMediaMuxer_new(fd, AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4);
} else if (gOutputFormat == FORMAT_WEBM) {
- muxer = new MediaMuxer(fd, MediaMuxer::OUTPUT_FORMAT_WEBM);
+ muxer = AMediaMuxer_new(fd, AMEDIAMUXER_OUTPUT_FORMAT_WEBM);
} else {
- muxer = new MediaMuxer(fd, MediaMuxer::OUTPUT_FORMAT_THREE_GPP);
+ muxer = AMediaMuxer_new(fd, AMEDIAMUXER_OUTPUT_FORMAT_THREE_GPP);
}
close(fd);
if (gRotate) {
- muxer->setOrientationHint(90); // TODO: does this do anything?
+ AMediaMuxer_setOrientationHint(muxer, 90); // TODO: does this do anything?
}
break;
}
@@ -774,8 +854,8 @@
}
} else {
// Main encoder loop.
- err = runEncoder(encoder, muxer, rawFp, mainDpy, dpy,
- mainDpyInfo.orientation);
+ err = runEncoder(encoder, muxer, rawFp, display, dpy,
+ displayInfo.orientation);
if (err != NO_ERROR) {
fprintf(stderr, "Encoder failed (err=%d)\n", err);
// fall through to cleanup
@@ -795,7 +875,7 @@
if (muxer != NULL) {
// If we don't stop muxer explicitly, i.e. let the destructor run,
// it may hang (b/11050628).
- err = muxer->stop();
+ err = AMediaMuxer_stop(muxer);
} else if (rawFp != stdout) {
fclose(rawFp);
}
@@ -941,6 +1021,9 @@
" in videos captured to illustrate bugs.\n"
"--time-limit TIME\n"
" Set the maximum recording time, in seconds. Default / maximum is %d.\n"
+ "--display-id ID\n"
+ " specify the physical display ID to record. Default is the primary display.\n"
+ " see \"dumpsys SurfaceFlinger --display-id\" for valid display IDs.\n"
"--verbose\n"
" Display interesting information on stdout.\n"
"--help\n"
@@ -972,9 +1055,18 @@
{ "monotonic-time", no_argument, NULL, 'm' },
{ "persistent-surface", no_argument, NULL, 'p' },
{ "bframes", required_argument, NULL, 'B' },
+ { "display-id", required_argument, NULL, 'd' },
{ NULL, 0, NULL, 0 }
};
+ std::optional<PhysicalDisplayId> displayId = SurfaceComposerClient::getInternalDisplayId();
+ if (!displayId) {
+ fprintf(stderr, "Failed to get token for internal display\n");
+ return 1;
+ }
+
+ gPhysicalDisplayId = *displayId;
+
while (true) {
int optionIndex = 0;
int ic = getopt_long(argc, argv, "", longOptions, &optionIndex);
@@ -1069,6 +1161,18 @@
return 2;
}
break;
+ case 'd':
+ gPhysicalDisplayId = atoll(optarg);
+ if (gPhysicalDisplayId == 0) {
+ fprintf(stderr, "Please specify a valid physical display id\n");
+ return 2;
+ } else if (SurfaceComposerClient::
+ getPhysicalDisplayToken(gPhysicalDisplayId) == nullptr) {
+ fprintf(stderr, "Invalid physical display id: %"
+ ANDROID_PHYSICAL_DISPLAY_ID_FORMAT "\n", gPhysicalDisplayId);
+ return 2;
+ }
+ break;
default:
if (ic != '?') {
fprintf(stderr, "getopt_long returned unexpected value 0x%x\n", ic);
diff --git a/cmds/screenrecord/screenrecord.h b/cmds/screenrecord/screenrecord.h
index 9b058c2..cec7c13 100644
--- a/cmds/screenrecord/screenrecord.h
+++ b/cmds/screenrecord/screenrecord.h
@@ -18,6 +18,6 @@
#define SCREENRECORD_SCREENRECORD_H
#define kVersionMajor 1
-#define kVersionMinor 2
+#define kVersionMinor 3
#endif /*SCREENRECORD_SCREENRECORD_H*/
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/audioloop.cpp b/cmds/stagefright/audioloop.cpp
index bd274d8..84a6d6b 100644
--- a/cmds/stagefright/audioloop.cpp
+++ b/cmds/stagefright/audioloop.cpp
@@ -107,8 +107,11 @@
if (useMic) {
// talk into the appropriate microphone for the duration
+ audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
+ attr.source = AUDIO_SOURCE_MIC;
+
source = new AudioSource(
- AUDIO_SOURCE_MIC,
+ &attr,
String16(),
sampleRate,
channels);
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 02ade94..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>
@@ -988,7 +988,7 @@
failed = false;
printf("getFrameAtTime(%s) => OK\n", filename);
- VideoFrame *frame = (VideoFrame *)mem->pointer();
+ VideoFrame *frame = (VideoFrame *)mem->unsecurePointer();
CHECK_EQ(writeJpegFile("/sdcard/out.jpg",
frame->getFlattenedData(),
diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp
index 22e2ef3..5b29158 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>
@@ -116,7 +116,7 @@
sp<IMemory> mem = mBuffers.itemAt(index);
- ssize_t n = read(mFd, mem->pointer(), mem->size());
+ ssize_t n = read(mFd, mem->unsecurePointer(), mem->size());
if (n <= 0) {
mListener->issueCommand(IStreamListener::EOS, false /* synchronous */);
} else {
@@ -238,7 +238,7 @@
copy = mem->size() - mCurrentBufferOffset;
}
- memcpy((uint8_t *)mem->pointer() + mCurrentBufferOffset, data, copy);
+ memcpy((uint8_t *)mem->unsecurePointer() + mCurrentBufferOffset, data, copy);
mCurrentBufferOffset += copy;
if (mCurrentBufferOffset == mem->size()) {
diff --git a/drm/drmserver/Android.bp b/drm/drmserver/Android.bp
index c25a0a1..fd71837 100644
--- a/drm/drmserver/Android.bp
+++ b/drm/drmserver/Android.bp
@@ -25,6 +25,7 @@
shared_libs: [
"libmedia",
+ "libmediametrics",
"libutils",
"liblog",
"libbinder",
diff --git a/drm/drmserver/DrmManager.cpp b/drm/drmserver/DrmManager.cpp
index afbcb39..38dc052 100644
--- a/drm/drmserver/DrmManager.cpp
+++ b/drm/drmserver/DrmManager.cpp
@@ -19,7 +19,10 @@
#include "utils/Log.h"
#include <utils/String8.h>
+
+#include <binder/IPCThreadState.h>
#include <drm/DrmInfo.h>
+
#include <drm/DrmInfoEvent.h>
#include <drm/DrmRights.h>
#include <drm/DrmConstraints.h>
@@ -28,6 +31,7 @@
#include <drm/DrmInfoRequest.h>
#include <drm/DrmSupportInfo.h>
#include <drm/DrmConvertedStatus.h>
+#include <media/MediaMetricsItem.h>
#include <IDrmEngine.h>
#include "DrmManager.h"
@@ -50,6 +54,39 @@
}
+void DrmManager::reportEngineMetrics(
+ const char func[], const String8& plugInId, const String8& mimeType) {
+ IDrmEngine& engine = mPlugInManager.getPlugIn(plugInId);
+
+ 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());
+
+ std::unique_ptr<DrmSupportInfo> info(engine.getSupportInfo(0));
+ if (NULL != info) {
+ item->setCString("description", info->getDescription().c_str());
+ }
+
+ if (!mimeType.isEmpty()) {
+ item->setCString("mime_types", mimeType.c_str());
+ } else if (NULL != info) {
+ DrmSupportInfo::MimeTypeIterator mimeIter = info->getMimeTypeIterator();
+ String8 mimes;
+ while (mimeIter.hasNext()) {
+ mimes += mimeIter.next();
+ if (mimeIter.hasNext()) {
+ mimes += ",";
+ }
+ }
+ item->setCString("mime_types", mimes.c_str());
+ }
+
+ if (!item->selfrecord()) {
+ ALOGE("Failed to record metrics");
+ }
+}
+
int DrmManager::addUniqueId(bool isNative) {
Mutex::Autolock _l(mLock);
@@ -147,27 +184,36 @@
for (size_t index = 0; index < plugInIdList.size(); index++) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInIdList.itemAt(index));
rDrmEngine.terminate(uniqueId);
+ reportEngineMetrics(__func__, plugInIdList[index]);
}
}
DrmConstraints* DrmManager::getConstraints(int uniqueId, const String8* path, const int action) {
Mutex::Autolock _l(mLock);
+ DrmConstraints *constraints = NULL;
const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, *path);
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
- return rDrmEngine.getConstraints(uniqueId, path, action);
+ constraints = rDrmEngine.getConstraints(uniqueId, path, action);
}
- return NULL;
+ if (NULL != constraints) {
+ reportEngineMetrics(__func__, plugInId);
+ }
+ return constraints;
}
DrmMetadata* DrmManager::getMetadata(int uniqueId, const String8* path) {
Mutex::Autolock _l(mLock);
+ DrmMetadata *meta = NULL;
const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, *path);
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
- return rDrmEngine.getMetadata(uniqueId, path);
+ meta = rDrmEngine.getMetadata(uniqueId, path);
}
- return NULL;
+ if (NULL != meta) {
+ reportEngineMetrics(__func__, plugInId);
+ }
+ return meta;
}
bool DrmManager::canHandle(int uniqueId, const String8& path, const String8& mimeType) {
@@ -175,6 +221,10 @@
const String8 plugInId = getSupportedPlugInId(mimeType);
bool result = (EMPTY_STRING != plugInId) ? true : false;
+ if (result) {
+ reportEngineMetrics(__func__, plugInId, mimeType);
+ }
+
if (0 < path.length()) {
if (result) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
@@ -191,12 +241,17 @@
DrmInfoStatus* DrmManager::processDrmInfo(int uniqueId, const DrmInfo* drmInfo) {
Mutex::Autolock _l(mLock);
- const String8 plugInId = getSupportedPlugInId(drmInfo->getMimeType());
+ DrmInfoStatus *infoStatus = NULL;
+ const String8 mimeType = drmInfo->getMimeType();
+ const String8 plugInId = getSupportedPlugInId(mimeType);
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
- return rDrmEngine.processDrmInfo(uniqueId, drmInfo);
+ infoStatus = rDrmEngine.processDrmInfo(uniqueId, drmInfo);
}
- return NULL;
+ if (NULL != infoStatus) {
+ reportEngineMetrics(__func__, plugInId, mimeType);
+ }
+ return infoStatus;
}
bool DrmManager::canHandle(int uniqueId, const String8& path) {
@@ -208,6 +263,7 @@
result = rDrmEngine.canHandle(uniqueId, path);
if (result) {
+ reportEngineMetrics(__func__, plugInPathList[i]);
break;
}
}
@@ -216,54 +272,75 @@
DrmInfo* DrmManager::acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) {
Mutex::Autolock _l(mLock);
- const String8 plugInId = getSupportedPlugInId(drmInfoRequest->getMimeType());
+ DrmInfo *info = NULL;
+ const String8 mimeType = drmInfoRequest->getMimeType();
+ const String8 plugInId = getSupportedPlugInId(mimeType);
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
- return rDrmEngine.acquireDrmInfo(uniqueId, drmInfoRequest);
+ info = rDrmEngine.acquireDrmInfo(uniqueId, drmInfoRequest);
}
- return NULL;
+ if (NULL != info) {
+ reportEngineMetrics(__func__, plugInId, mimeType);
+ }
+ return info;
}
status_t DrmManager::saveRights(int uniqueId, const DrmRights& drmRights,
const String8& rightsPath, const String8& contentPath) {
Mutex::Autolock _l(mLock);
- const String8 plugInId = getSupportedPlugInId(drmRights.getMimeType());
+ const String8 mimeType = drmRights.getMimeType();
+ const String8 plugInId = getSupportedPlugInId(mimeType);
status_t result = DRM_ERROR_UNKNOWN;
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
result = rDrmEngine.saveRights(uniqueId, drmRights, rightsPath, contentPath);
}
+ if (DRM_NO_ERROR == result) {
+ reportEngineMetrics(__func__, plugInId, mimeType);
+ }
return result;
}
String8 DrmManager::getOriginalMimeType(int uniqueId, const String8& path, int fd) {
Mutex::Autolock _l(mLock);
+ String8 mimeType(EMPTY_STRING);
const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, path);
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
- return rDrmEngine.getOriginalMimeType(uniqueId, path, fd);
+ mimeType = rDrmEngine.getOriginalMimeType(uniqueId, path, fd);
}
- return EMPTY_STRING;
+ if (!mimeType.isEmpty()) {
+ reportEngineMetrics(__func__, plugInId, mimeType);
+ }
+ return mimeType;
}
int DrmManager::getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType) {
Mutex::Autolock _l(mLock);
+ int type = DrmObjectType::UNKNOWN;
const String8 plugInId = getSupportedPlugInId(uniqueId, path, mimeType);
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
- return rDrmEngine.getDrmObjectType(uniqueId, path, mimeType);
+ type = rDrmEngine.getDrmObjectType(uniqueId, path, mimeType);
}
- return DrmObjectType::UNKNOWN;
+ if (DrmObjectType::UNKNOWN != type) {
+ reportEngineMetrics(__func__, plugInId, mimeType);
+ }
+ return type;
}
int DrmManager::checkRightsStatus(int uniqueId, const String8& path, int action) {
Mutex::Autolock _l(mLock);
+ int rightsStatus = RightsStatus::RIGHTS_INVALID;
const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, path);
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
- return rDrmEngine.checkRightsStatus(uniqueId, path, action);
+ rightsStatus = rDrmEngine.checkRightsStatus(uniqueId, path, action);
}
- return RightsStatus::RIGHTS_INVALID;
+ if (RightsStatus::RIGHTS_INVALID != rightsStatus) {
+ reportEngineMetrics(__func__, plugInId);
+ }
+ return rightsStatus;
}
status_t DrmManager::consumeRights(
@@ -307,6 +384,9 @@
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
result = rDrmEngine.removeRights(uniqueId, path);
}
+ if (DRM_NO_ERROR == result) {
+ reportEngineMetrics(__func__, plugInId);
+ }
return result;
}
@@ -319,6 +399,7 @@
if (DRM_NO_ERROR != result) {
break;
}
+ reportEngineMetrics(__func__, plugInIdList[index]);
}
return result;
}
@@ -335,6 +416,7 @@
++mConvertId;
convertId = mConvertId;
mConvertSessionMap.add(convertId, &rDrmEngine);
+ reportEngineMetrics(__func__, plugInId, mimeType);
}
}
return convertId;
@@ -415,6 +497,7 @@
if (DRM_NO_ERROR == result) {
++mDecryptSessionId;
mDecryptSessionMap.add(mDecryptSessionId, &rDrmEngine);
+ reportEngineMetrics(__func__, plugInId, String8(mime));
break;
}
}
@@ -443,6 +526,7 @@
if (DRM_NO_ERROR == result) {
++mDecryptSessionId;
mDecryptSessionMap.add(mDecryptSessionId, &rDrmEngine);
+ reportEngineMetrics(__func__, plugInId, String8(mime));
break;
}
}
@@ -472,6 +556,7 @@
if (DRM_NO_ERROR == result) {
++mDecryptSessionId;
mDecryptSessionMap.add(mDecryptSessionId, &rDrmEngine);
+ reportEngineMetrics(__func__, plugInId, mimeType);
break;
}
}
diff --git a/drm/drmserver/DrmManager.h b/drm/drmserver/DrmManager.h
index 26222bc..75fc1a8 100644
--- a/drm/drmserver/DrmManager.h
+++ b/drm/drmserver/DrmManager.h
@@ -143,6 +143,9 @@
bool canHandle(int uniqueId, const String8& path);
+ void reportEngineMetrics(const char func[],
+ const String8& plugInId, const String8& mimeType = String8(""));
+
private:
enum {
kMaxNumUniqueIds = 0x1000,
diff --git a/drm/libmediadrm/Android.bp b/drm/libmediadrm/Android.bp
index 52c7438..255640f 100644
--- a/drm/libmediadrm/Android.bp
+++ b/drm/libmediadrm/Android.bp
@@ -17,13 +17,10 @@
srcs: [
"DrmPluginPath.cpp",
"DrmSessionManager.cpp",
- "ICrypto.cpp",
- "IDrm.cpp",
- "IDrmClient.cpp",
- "IMediaDrmService.cpp",
"SharedLibrary.cpp",
"DrmHal.cpp",
"CryptoHal.cpp",
+ "DrmUtils.cpp",
],
local_include_dirs: [
@@ -40,15 +37,14 @@
],
shared_libs: [
- "libbinder",
+ "libbinder_ndk",
"libcutils",
"libdl",
"liblog",
"libmedia",
"libmediadrmmetrics_lite",
- "libmediametrics",
+ "libmediametrics#1",
"libmediautils",
- "libresourcemanagerservice",
"libstagefright_foundation",
"libutils",
"android.hardware.drm@1.0",
@@ -58,6 +54,16 @@
"libhidlbase",
],
+ static_libs: [
+ "resourcemanager_aidl_interface-ndk_platform",
+ ],
+
+ export_shared_lib_headers: [
+ "android.hardware.drm@1.0",
+ "android.hardware.drm@1.1",
+ "android.hardware.drm@1.2",
+ ],
+
cflags: [
"-Werror",
"-Wall",
@@ -88,8 +94,6 @@
"android.hardware.drm@1.0",
"android.hardware.drm@1.1",
"android.hardware.drm@1.2",
- "libbinder",
- "libhidlbase",
"liblog",
"libmediametrics",
"libprotobuf-cpp-lite",
@@ -127,8 +131,6 @@
"android.hardware.drm@1.1",
"android.hardware.drm@1.2",
"libbase",
- "libbinder",
- "libhidlbase",
"liblog",
"libmediametrics",
"libprotobuf-cpp-full",
@@ -142,3 +144,29 @@
],
}
+cc_library_shared {
+ name: "libmediadrmmetrics_consumer",
+ srcs: [
+ "DrmMetricsConsumer.cpp",
+ ],
+
+ include_dirs: [
+ "frameworks/av/media/libmedia/include"
+ ],
+
+ shared_libs: [
+ "android.hardware.drm@1.0",
+ "android.hardware.drm@1.1",
+ "android.hardware.drm@1.2",
+ "libbinder",
+ "libhidlbase",
+ "liblog",
+ "libmediadrm",
+ "libmediadrmmetrics_full",
+ "libutils",
+ ],
+
+ header_libs: [
+ "libstagefright_foundation_headers",
+ ],
+}
diff --git a/drm/libmediadrm/CryptoHal.cpp b/drm/libmediadrm/CryptoHal.cpp
index d62ccd6..18772e0 100644
--- a/drm/libmediadrm/CryptoHal.cpp
+++ b/drm/libmediadrm/CryptoHal.cpp
@@ -19,9 +19,8 @@
#include <utils/Log.h>
#include <android/hardware/drm/1.0/types.h>
-#include <android/hidl/manager/1.0/IServiceManager.h>
-
-#include <binder/IMemory.h>
+#include <android/hidl/manager/1.2/IServiceManager.h>
+#include <hidl/ServiceManagement.h>
#include <hidlmemory/FrameworkUtils.h>
#include <media/hardware/CryptoAPI.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -45,9 +44,9 @@
using ::android::hardware::hidl_memory;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
+using ::android::hardware::HidlMemory;
using ::android::hardware::Return;
using ::android::hardware::Void;
-using ::android::hidl::manager::V1_0::IServiceManager;
using ::android::sp;
typedef drm::V1_2::Status Status_V1_2;
@@ -119,7 +118,6 @@
CryptoHal::CryptoHal()
: mFactories(makeCryptoFactories()),
mInitCheck((mFactories.size() == 0) ? ERROR_UNSUPPORTED : NO_INIT),
- mNextBufferId(0),
mHeapSeqNum(0) {
}
@@ -129,9 +127,9 @@
Vector<sp<ICryptoFactory>> CryptoHal::makeCryptoFactories() {
Vector<sp<ICryptoFactory>> factories;
- auto manager = ::IServiceManager::getService();
+ auto manager = hardware::defaultServiceManager1_2();
if (manager != NULL) {
- manager->listByInterface(drm::V1_0::ICryptoFactory::descriptor,
+ manager->listManifestByInterface(drm::V1_0::ICryptoFactory::descriptor,
[&factories](const hidl_vec<hidl_string> ®istered) {
for (const auto &instance : registered) {
auto factory = drm::V1_0::ICryptoFactory::getService(instance);
@@ -142,7 +140,7 @@
}
}
);
- manager->listByInterface(drm::V1_1::ICryptoFactory::descriptor,
+ manager->listManifestByInterface(drm::V1_1::ICryptoFactory::descriptor,
[&factories](const hidl_vec<hidl_string> ®istered) {
for (const auto &instance : registered) {
auto factory = drm::V1_1::ICryptoFactory::getService(instance);
@@ -252,26 +250,23 @@
/**
- * If the heap base isn't set, get the heap base from the IMemory
+ * If the heap base isn't set, get the heap base from the HidlMemory
* and send it to the HAL so it can map a remote heap of the same
* size. Once the heap base is established, shared memory buffers
* are sent by providing an offset into the heap and a buffer size.
*/
-int32_t CryptoHal::setHeapBase(const sp<IMemoryHeap>& heap) {
- using ::android::hardware::fromHeap;
- using ::android::hardware::HidlMemory;
-
- if (heap == NULL) {
- ALOGE("setHeapBase(): heap is NULL");
+int32_t CryptoHal::setHeapBase(const sp<HidlMemory>& heap) {
+ if (heap == NULL || mHeapSeqNum < 0) {
+ ALOGE("setHeapBase(): heap %p mHeapSeqNum %d", heap.get(), mHeapSeqNum);
return -1;
}
Mutex::Autolock autoLock(mLock);
int32_t seqNum = mHeapSeqNum++;
- sp<HidlMemory> hidlMemory = fromHeap(heap);
- mHeapBases.add(seqNum, HeapBase(mNextBufferId, heap->getSize()));
- Return<void> hResult = mPlugin->setSharedBufferBase(*hidlMemory, mNextBufferId++);
+ uint32_t bufferId = static_cast<uint32_t>(seqNum);
+ mHeapSizes.add(seqNum, heap->size());
+ Return<void> hResult = mPlugin->setSharedBufferBase(*heap, bufferId);
ALOGE_IF(!hResult.isOk(), "setSharedBufferBase(): remote call failed");
return seqNum;
}
@@ -286,60 +281,40 @@
* TODO: Add a releaseSharedBuffer method in a future DRM HAL
* API version to make this explicit.
*/
- ssize_t index = mHeapBases.indexOfKey(seqNum);
+ ssize_t index = mHeapSizes.indexOfKey(seqNum);
if (index >= 0) {
if (mPlugin != NULL) {
- uint32_t bufferId = mHeapBases[index].getBufferId();
+ uint32_t bufferId = static_cast<uint32_t>(seqNum);
Return<void> hResult = mPlugin->setSharedBufferBase(hidl_memory(), bufferId);
ALOGE_IF(!hResult.isOk(), "setSharedBufferBase(): remote call failed");
}
- mHeapBases.removeItem(seqNum);
+ mHeapSizes.removeItem(seqNum);
}
}
-status_t CryptoHal::toSharedBuffer(const sp<IMemory>& memory, int32_t seqNum, ::SharedBuffer* buffer) {
- ssize_t offset;
- size_t size;
-
- if (memory == NULL || buffer == NULL) {
- return UNEXPECTED_NULL;
- }
-
- sp<IMemoryHeap> heap = memory->getMemory(&offset, &size);
- if (heap == NULL) {
- return UNEXPECTED_NULL;
- }
-
+status_t CryptoHal::checkSharedBuffer(const ::SharedBuffer &buffer) {
+ int32_t seqNum = static_cast<int32_t>(buffer.bufferId);
// memory must be in one of the heaps that have been set
- if (mHeapBases.indexOfKey(seqNum) < 0) {
+ if (mHeapSizes.indexOfKey(seqNum) < 0) {
return UNKNOWN_ERROR;
}
- // heap must be the same size as the one that was set in setHeapBase
- if (mHeapBases.valueFor(seqNum).getSize() != heap->getSize()) {
- android_errorWriteLog(0x534e4554, "76221123");
- return UNKNOWN_ERROR;
- }
-
// memory must be within the address space of the heap
- if (memory->pointer() != static_cast<uint8_t *>(heap->getBase()) + memory->offset() ||
- heap->getSize() < memory->offset() + memory->size() ||
- SIZE_MAX - memory->offset() < memory->size()) {
+ size_t heapSize = mHeapSizes.valueFor(seqNum);
+ if (heapSize < buffer.offset + buffer.size ||
+ SIZE_MAX - buffer.offset < buffer.size) {
android_errorWriteLog(0x534e4554, "76221123");
return UNKNOWN_ERROR;
}
- buffer->bufferId = mHeapBases.valueFor(seqNum).getBufferId();
- buffer->offset = offset >= 0 ? offset : 0;
- buffer->size = size;
return OK;
}
ssize_t CryptoHal::decrypt(const uint8_t keyId[16], const uint8_t iv[16],
CryptoPlugin::Mode mode, const CryptoPlugin::Pattern &pattern,
- const ICrypto::SourceBuffer &source, size_t offset,
+ const ::SharedBuffer &hSource, size_t offset,
const CryptoPlugin::SubSample *subSamples, size_t numSubSamples,
- const ICrypto::DestinationBuffer &destination, AString *errorDetailMsg) {
+ const ::DestinationBuffer &hDestination, AString *errorDetailMsg) {
Mutex::Autolock autoLock(mLock);
if (mInitCheck != OK) {
@@ -377,28 +352,21 @@
}
auto hSubSamples = hidl_vec<SubSample>(stdSubSamples);
- int32_t heapSeqNum = source.mHeapSeqNum;
bool secure;
- ::DestinationBuffer hDestination;
- if (destination.mType == kDestinationTypeSharedMemory) {
- hDestination.type = BufferType::SHARED_MEMORY;
- status_t status = toSharedBuffer(destination.mSharedMemory, heapSeqNum,
- &hDestination.nonsecureMemory);
+ if (hDestination.type == BufferType::SHARED_MEMORY) {
+ status_t status = checkSharedBuffer(hDestination.nonsecureMemory);
if (status != OK) {
return status;
}
secure = false;
- } else if (destination.mType == kDestinationTypeNativeHandle) {
- hDestination.type = BufferType::NATIVE_HANDLE;
- hDestination.secureMemory = hidl_handle(destination.mHandle);
+ } else if (hDestination.type == BufferType::NATIVE_HANDLE) {
secure = true;
} else {
android_errorWriteLog(0x534e4554, "70526702");
return UNKNOWN_ERROR;
}
- ::SharedBuffer hSource;
- status_t status = toSharedBuffer(source.mSharedMemory, heapSeqNum, &hSource);
+ status_t status = checkSharedBuffer(hSource);
if (status != OK) {
return status;
}
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index a2234e6..5b32a04 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -20,14 +20,14 @@
#include <utils/Log.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
+#include <android/binder_manager.h>
+#include <aidl/android/media/BnResourceManagerClient.h>
#include <android/hardware/drm/1.2/types.h>
#include <android/hidl/manager/1.2/IServiceManager.h>
#include <hidl/ServiceManagement.h>
-
#include <media/EventMetric.h>
+#include <media/MediaMetrics.h>
#include <media/PluginMetricsReporting.h>
#include <media/drm/DrmAPI.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -38,6 +38,10 @@
#include <mediadrm/DrmHal.h>
#include <mediadrm/DrmSessionClientInterface.h>
#include <mediadrm/DrmSessionManager.h>
+#include <mediadrm/IDrmMetricsConsumer.h>
+#include <mediadrm/DrmUtils.h>
+
+#include <vector>
using drm::V1_0::KeyedVector;
using drm::V1_0::KeyRequestType;
@@ -57,7 +61,6 @@
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::Void;
-using ::android::hidl::manager::V1_0::IServiceManager;
using ::android::os::PersistableBundle;
using ::android::sp;
@@ -97,17 +100,6 @@
#define INIT_CHECK() {if (mInitCheck != OK) return mInitCheck;}
-static inline int getCallingPid() {
- return IPCThreadState::self()->getCallingPid();
-}
-
-static bool checkPermission(const char* permissionString) {
- if (getpid() == IPCThreadState::self()->getCallingPid()) return true;
- bool ok = checkCallingPermission(String16(permissionString));
- if (!ok) ALOGE("Request requires %s", permissionString);
- return ok;
-}
-
static const Vector<uint8_t> toVector(const hidl_vec<uint8_t> &vec) {
Vector<uint8_t> vector;
vector.appendArray(vec.data(), vec.size());
@@ -297,21 +289,43 @@
Mutex DrmHal::mLock;
-bool DrmHal::DrmSessionClient::reclaimResource() {
+struct DrmHal::DrmSessionClient : public aidl::android::media::BnResourceManagerClient {
+ explicit DrmSessionClient(DrmHal* drm, const Vector<uint8_t>& sessionId)
+ : mSessionId(sessionId),
+ mDrm(drm) {}
+
+ ::ndk::ScopedAStatus reclaimResource(bool* _aidl_return) override;
+ ::ndk::ScopedAStatus getName(::std::string* _aidl_return) override;
+
+ const Vector<uint8_t> mSessionId;
+
+ virtual ~DrmSessionClient();
+
+private:
+ wp<DrmHal> mDrm;
+
+ DISALLOW_EVIL_CONSTRUCTORS(DrmSessionClient);
+};
+
+::ndk::ScopedAStatus DrmHal::DrmSessionClient::reclaimResource(bool* _aidl_return) {
+ auto sessionId = mSessionId;
sp<DrmHal> drm = mDrm.promote();
if (drm == NULL) {
- return true;
+ *_aidl_return = true;
+ return ::ndk::ScopedAStatus::ok();
}
- status_t err = drm->closeSession(mSessionId);
+ status_t err = drm->closeSession(sessionId);
if (err != OK) {
- return false;
+ *_aidl_return = false;
+ return ::ndk::ScopedAStatus::ok();
}
drm->sendEvent(EventType::SESSION_RECLAIMED,
- toHidlVec(mSessionId), hidl_vec<uint8_t>());
- return true;
+ toHidlVec(sessionId), hidl_vec<uint8_t>());
+ *_aidl_return = true;
+ return ::ndk::ScopedAStatus::ok();
}
-String8 DrmHal::DrmSessionClient::getName() {
+::ndk::ScopedAStatus DrmHal::DrmSessionClient::getName(::std::string* _aidl_return) {
String8 name;
sp<DrmHal> drm = mDrm.promote();
if (drm == NULL) {
@@ -325,7 +339,8 @@
name.appendFormat("%02x", mSessionId[i]);
}
name.append("]");
- return name;
+ *_aidl_return = name;
+ return ::ndk::ScopedAStatus::ok();
}
DrmHal::DrmSessionClient::~DrmSessionClient() {
@@ -374,44 +389,8 @@
mPluginV1_2.clear();
}
-Vector<sp<IDrmFactory>> DrmHal::makeDrmFactories() {
- Vector<sp<IDrmFactory>> factories;
-
- auto manager = hardware::defaultServiceManager1_2();
-
- if (manager != NULL) {
- manager->listManifestByInterface(drm::V1_0::IDrmFactory::descriptor,
- [&factories](const hidl_vec<hidl_string> ®istered) {
- for (const auto &instance : registered) {
- auto factory = drm::V1_0::IDrmFactory::getService(instance);
- if (factory != NULL) {
- factories.push_back(factory);
- }
- }
- }
- );
- manager->listManifestByInterface(drm::V1_1::IDrmFactory::descriptor,
- [&factories](const hidl_vec<hidl_string> ®istered) {
- for (const auto &instance : registered) {
- auto factory = drm::V1_1::IDrmFactory::getService(instance);
- if (factory != NULL) {
- factories.push_back(factory);
- }
- }
- }
- );
- manager->listByInterface(drm::V1_2::IDrmFactory::descriptor,
- [&factories](const hidl_vec<hidl_string> ®istered) {
- for (const auto &instance : registered) {
- auto factory = drm::V1_2::IDrmFactory::getService(instance);
- if (factory != NULL) {
- factories.push_back(factory);
- }
- }
- }
- );
- }
-
+std::vector<sp<IDrmFactory>> DrmHal::makeDrmFactories() {
+ std::vector<sp<IDrmFactory>> factories(DrmUtils::MakeDrmFactories());
if (factories.size() == 0) {
// must be in passthrough mode, load the default passthrough service
auto passthrough = IDrmFactory::getService();
@@ -429,6 +408,7 @@
const uint8_t uuid[16], const String8& appPackageName) {
mAppPackageName = appPackageName;
mMetrics.SetAppPackageName(appPackageName);
+ mMetrics.SetAppUid(AIBinder_getCallingUid());
sp<IDrmPlugin> plugin;
Return<void> hResult = factory->createPlugin(uuid, appPackageName.string(),
@@ -455,12 +435,6 @@
status_t DrmHal::setListener(const sp<IDrmClient>& listener)
{
Mutex::Autolock lock(mEventLock);
- if (mListener != NULL){
- IInterface::asBinder(mListener)->unlinkToDeath(this);
- }
- if (listener != NULL) {
- IInterface::asBinder(listener)->linkToDeath(this);
- }
mListener = listener;
return NO_ERROR;
}
@@ -474,10 +448,6 @@
mEventLock.unlock();
if (listener != NULL) {
- Parcel obj;
- writeByteArray(obj, sessionId);
- writeByteArray(obj, data);
-
Mutex::Autolock lock(mNotifyLock);
DrmPlugin::EventType eventType;
switch(hEventType) {
@@ -499,7 +469,7 @@
default:
return Void();
}
- listener->notify(eventType, 0, &obj);
+ listener->sendEvent(eventType, sessionId, data);
}
return Void();
}
@@ -512,12 +482,8 @@
mEventLock.unlock();
if (listener != NULL) {
- Parcel obj;
- writeByteArray(obj, sessionId);
- obj.writeInt64(expiryTimeInMS);
-
Mutex::Autolock lock(mNotifyLock);
- listener->notify(DrmPlugin::kDrmPluginEventExpirationUpdate, 0, &obj);
+ listener->sendExpirationUpdate(sessionId, expiryTimeInMS);
}
return Void();
}
@@ -534,21 +500,17 @@
}
Return<void> DrmHal::sendKeysChange_1_2(const hidl_vec<uint8_t>& sessionId,
- const hidl_vec<KeyStatus>& keyStatusList, bool hasNewUsableKey) {
+ const hidl_vec<KeyStatus>& hKeyStatusList, bool hasNewUsableKey) {
mEventLock.lock();
sp<IDrmClient> listener = mListener;
mEventLock.unlock();
if (listener != NULL) {
- Parcel obj;
- writeByteArray(obj, sessionId);
-
- size_t nKeys = keyStatusList.size();
- obj.writeInt32(nKeys);
+ std::vector<DrmKeyStatus> keyStatusList;
+ size_t nKeys = hKeyStatusList.size();
for (size_t i = 0; i < nKeys; ++i) {
- const KeyStatus &keyStatus = keyStatusList[i];
- writeByteArray(obj, keyStatus.keyId);
+ const KeyStatus &keyStatus = hKeyStatusList[i];
uint32_t type;
switch(keyStatus.type) {
case KeyStatusType::USABLE:
@@ -571,19 +533,18 @@
type = DrmPlugin::kKeyStatusType_InternalError;
break;
}
- obj.writeInt32(type);
+ keyStatusList.push_back({type, keyStatus.keyId});
mMetrics.mKeyStatusChangeCounter.Increment(keyStatus.type);
}
- obj.writeInt32(hasNewUsableKey);
Mutex::Autolock lock(mNotifyLock);
- listener->notify(DrmPlugin::kDrmPluginEventKeysChange, 0, &obj);
+ listener->sendKeysChange(sessionId, keyStatusList, hasNewUsableKey);
} else {
// There's no listener. But we still want to count the key change
// events.
- size_t nKeys = keyStatusList.size();
+ size_t nKeys = hKeyStatusList.size();
for (size_t i = 0; i < nKeys; i++) {
- mMetrics.mKeyStatusChangeCounter.Increment(keyStatusList[i].type);
+ mMetrics.mKeyStatusChangeCounter.Increment(hKeyStatusList[i].type);
}
}
@@ -598,10 +559,8 @@
mEventLock.unlock();
if (listener != NULL) {
- Parcel obj;
- writeByteArray(obj, sessionId);
Mutex::Autolock lock(mNotifyLock);
- listener->notify(DrmPlugin::kDrmPluginEventSessionLostState, 0, &obj);
+ listener->sendSessionLostState(sessionId);
}
return Void();
}
@@ -746,7 +705,7 @@
// reclaimSession may call back to closeSession, since mLock is
// shared between Drm instances, we should unlock here to avoid
// deadlock.
- retry = DrmSessionManager::Instance()->reclaimSession(getCallingPid());
+ retry = DrmSessionManager::Instance()->reclaimSession(AIBinder_getCallingPid());
mLock.lock();
} else {
retry = false;
@@ -754,9 +713,10 @@
} while (retry);
if (err == OK) {
- sp<DrmSessionClient> client(new DrmSessionClient(this, sessionId));
- DrmSessionManager::Instance()->addSession(getCallingPid(), client, sessionId);
- mOpenSessions.push(client);
+ std::shared_ptr<DrmSessionClient> client(new DrmSessionClient(this, sessionId));
+ DrmSessionManager::Instance()->addSession(AIBinder_getCallingPid(),
+ std::static_pointer_cast<IResourceManagerClient>(client), sessionId);
+ mOpenSessions.push_back(client);
mMetrics.SetSessionStart(sessionId);
}
@@ -772,9 +732,9 @@
if (status.isOk()) {
if (status == Status::OK) {
DrmSessionManager::Instance()->removeSession(sessionId);
- for (size_t i = 0; i < mOpenSessions.size(); i++) {
- if (isEqualSessionId(mOpenSessions[i]->mSessionId, sessionId)) {
- mOpenSessions.removeAt(i);
+ for (auto i = mOpenSessions.begin(); i != mOpenSessions.end(); i++) {
+ if (isEqualSessionId((*i)->mSessionId, sessionId)) {
+ mOpenSessions.erase(i);
break;
}
}
@@ -1368,11 +1328,11 @@
return status.isOk() ? toStatusT(status) : DEAD_OBJECT;
}
-status_t DrmHal::getMetrics(PersistableBundle* metrics) {
- if (metrics == nullptr) {
+status_t DrmHal::getMetrics(const sp<IDrmMetricsConsumer> &consumer) {
+ if (consumer == nullptr) {
return UNEXPECTED_NULL;
}
- mMetrics.Export(metrics);
+ consumer->consumeFrameworkMetrics(mMetrics);
// Append vendor metrics if they are supported.
if (mPluginV1_1 != NULL) {
@@ -1399,11 +1359,7 @@
if (status != Status::OK) {
ALOGV("Error getting plugin metrics: %d", status);
} else {
- PersistableBundle pluginBundle;
- if (MediaDrmMetrics::HidlMetricsToBundle(
- pluginMetrics, &pluginBundle) == OK) {
- metrics->putPersistableBundle(String16(vendor), pluginBundle);
- }
+ consumer->consumeHidlMetrics(vendor, pluginMetrics);
}
err = toStatusT(status);
});
@@ -1537,10 +1493,6 @@
Mutex::Autolock autoLock(mLock);
INIT_CHECK();
- if (!checkPermission("android.permission.ACCESS_DRM_CERTIFICATES")) {
- return -EPERM;
- }
-
DrmSessionManager::Instance()->useSession(sessionId);
status_t err = UNKNOWN_ERROR;
@@ -1558,39 +1510,23 @@
return hResult.isOk() ? err : DEAD_OBJECT;
}
-void DrmHal::binderDied(const wp<IBinder> &the_late_who __unused)
-{
- cleanup();
-}
-
-void DrmHal::writeByteArray(Parcel &obj, hidl_vec<uint8_t> const &vec)
-{
- if (vec.size()) {
- obj.writeInt32(vec.size());
- obj.write(vec.data(), vec.size());
- } else {
- obj.writeInt32(0);
- }
-}
-
void DrmHal::reportFrameworkMetrics() const
{
- std::unique_ptr<MediaAnalyticsItem> item(MediaAnalyticsItem::create("mediadrm"));
- item->generateSessionID();
- item->setPkgName(mMetrics.GetAppPackageName().c_str());
+ mediametrics_handle_t item(mediametrics_create("mediadrm"));
+ mediametrics_setUid(item, mMetrics.GetAppUid());
String8 vendor;
String8 description;
status_t result = getPropertyStringInternal(String8("vendor"), vendor);
if (result != OK) {
ALOGE("Failed to get vendor from drm plugin: %d", result);
} else {
- item->setCString("vendor", vendor.c_str());
+ mediametrics_setCString(item, "vendor", vendor.c_str());
}
result = getPropertyStringInternal(String8("description"), description);
if (result != OK) {
ALOGE("Failed to get description from drm plugin: %d", result);
} else {
- item->setCString("description", description.c_str());
+ mediametrics_setCString(item, "description", description.c_str());
}
std::string serializedMetrics;
@@ -1601,11 +1537,12 @@
std::string b64EncodedMetrics = toBase64StringNoPad(serializedMetrics.data(),
serializedMetrics.size());
if (!b64EncodedMetrics.empty()) {
- item->setCString("serialized_metrics", b64EncodedMetrics.c_str());
+ mediametrics_setCString(item, "serialized_metrics", b64EncodedMetrics.c_str());
}
- if (!item->selfrecord()) {
+ if (!mediametrics_selfRecord(item)) {
ALOGE("Failed to self record framework metrics");
}
+ mediametrics_delete(item);
}
void DrmHal::reportPluginMetrics() const
@@ -1619,7 +1556,7 @@
std::string metricsString = toBase64StringNoPad(metricsVector.array(),
metricsVector.size());
status_t res = android::reportDrmPluginMetrics(metricsString, vendor,
- description, mAppPackageName);
+ description, mMetrics.GetAppUid());
if (res != OK) {
ALOGE("Metrics were retrieved but could not be reported: %d", res);
}
diff --git a/drm/libmediadrm/DrmMetrics.cpp b/drm/libmediadrm/DrmMetrics.cpp
index 3080802..996fd19 100644
--- a/drm/libmediadrm/DrmMetrics.cpp
+++ b/drm/libmediadrm/DrmMetrics.cpp
@@ -29,143 +29,12 @@
using ::android::String16;
using ::android::String8;
using ::android::drm_metrics::DrmFrameworkMetrics;
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
using ::android::hardware::drm::V1_0::EventType;
using ::android::hardware::drm::V1_2::KeyStatusType;
using ::android::hardware::drm::V1_1::DrmMetricGroup;
-using ::android::os::PersistableBundle;
namespace {
-template <typename T> std::string GetAttributeName(T type);
-
-template <> std::string GetAttributeName<KeyStatusType>(KeyStatusType type) {
- static const char *type_names[] = {"USABLE", "EXPIRED",
- "OUTPUT_NOT_ALLOWED", "STATUS_PENDING",
- "INTERNAL_ERROR"};
- if (((size_t)type) > arraysize(type_names)) {
- return "UNKNOWN_TYPE";
- }
- return type_names[(size_t)type];
-}
-
-template <> std::string GetAttributeName<EventType>(EventType type) {
- static const char *type_names[] = {"PROVISION_REQUIRED", "KEY_NEEDED",
- "KEY_EXPIRED", "VENDOR_DEFINED",
- "SESSION_RECLAIMED"};
- if (((size_t)type) > arraysize(type_names)) {
- return "UNKNOWN_TYPE";
- }
- return type_names[(size_t)type];
-}
-
-template <typename T>
-void ExportCounterMetric(const android::CounterMetric<T> &counter,
- PersistableBundle *metrics) {
- if (!metrics) {
- ALOGE("metrics was unexpectedly null.");
- return;
- }
- std::string success_count_name = counter.metric_name() + ".ok.count";
- std::string error_count_name = counter.metric_name() + ".error.count";
- std::vector<int64_t> status_values;
- counter.ExportValues(
- [&](const android::status_t status, const int64_t value) {
- if (status == android::OK) {
- metrics->putLong(android::String16(success_count_name.c_str()),
- value);
- } else {
- int64_t total_errors(0);
- metrics->getLong(android::String16(error_count_name.c_str()),
- &total_errors);
- metrics->putLong(android::String16(error_count_name.c_str()),
- total_errors + value);
- status_values.push_back(status);
- }
- });
- if (!status_values.empty()) {
- std::string error_list_name = counter.metric_name() + ".error.list";
- metrics->putLongVector(android::String16(error_list_name.c_str()),
- status_values);
- }
-}
-
-template <typename T>
-void ExportCounterMetricWithAttributeNames(
- const android::CounterMetric<T> &counter, PersistableBundle *metrics) {
- if (!metrics) {
- ALOGE("metrics was unexpectedly null.");
- return;
- }
- counter.ExportValues([&](const T &attribute, const int64_t value) {
- std::string name = counter.metric_name() + "." +
- GetAttributeName(attribute) + ".count";
- metrics->putLong(android::String16(name.c_str()), value);
- });
-}
-
-template <typename T>
-void ExportEventMetric(const android::EventMetric<T> &event,
- PersistableBundle *metrics) {
- if (!metrics) {
- ALOGE("metrics was unexpectedly null.");
- return;
- }
- std::string success_count_name = event.metric_name() + ".ok.count";
- std::string error_count_name = event.metric_name() + ".error.count";
- std::string timing_name = event.metric_name() + ".ok.average_time_micros";
- std::vector<int64_t> status_values;
- event.ExportValues([&](const android::status_t &status,
- const android::EventStatistics &value) {
- if (status == android::OK) {
- metrics->putLong(android::String16(success_count_name.c_str()),
- value.count);
- metrics->putLong(android::String16(timing_name.c_str()),
- value.mean);
- } else {
- int64_t total_errors(0);
- metrics->getLong(android::String16(error_count_name.c_str()),
- &total_errors);
- metrics->putLong(android::String16(error_count_name.c_str()),
- total_errors + value.count);
- status_values.push_back(status);
- }
- });
- if (!status_values.empty()) {
- std::string error_list_name = event.metric_name() + ".error.list";
- metrics->putLongVector(android::String16(error_list_name.c_str()),
- status_values);
- }
-}
-
-void ExportSessionLifespans(
- const std::map<std::string, std::pair<int64_t, int64_t>> &mSessionLifespans,
- PersistableBundle *metrics) {
- if (!metrics) {
- ALOGE("metrics was unexpectedly null.");
- return;
- }
-
- if (mSessionLifespans.empty()) {
- return;
- }
-
- PersistableBundle startTimesBundle;
- PersistableBundle endTimesBundle;
- for (auto it = mSessionLifespans.begin(); it != mSessionLifespans.end();
- it++) {
- String16 key(it->first.c_str(), it->first.size());
- startTimesBundle.putLong(key, it->second.first);
- endTimesBundle.putLong(key, it->second.second);
- }
- metrics->putPersistableBundle(
- android::String16("drm.mediadrm.session_start_times_ms"),
- startTimesBundle);
- metrics->putPersistableBundle(
- android::String16("drm.mediadrm.session_end_times_ms"), endTimesBundle);
-}
-
std::string ToHexString(const android::Vector<uint8_t> &sessionId) {
std::ostringstream out;
out << std::hex << std::setfill('0');
@@ -175,31 +44,6 @@
return out.str();
}
-template <typename CT>
-void SetValue(const String16 &name, DrmMetricGroup::ValueType type,
- const CT &value, PersistableBundle *bundle) {
- switch (type) {
- case DrmMetricGroup::ValueType::INT64_TYPE:
- bundle->putLong(name, value.int64Value);
- break;
- case DrmMetricGroup::ValueType::DOUBLE_TYPE:
- bundle->putDouble(name, value.doubleValue);
- break;
- case DrmMetricGroup::ValueType::STRING_TYPE:
- bundle->putString(name, String16(value.stringValue.c_str()));
- break;
- default:
- ALOGE("Unexpected value type: %hhu", type);
- }
-}
-
-inline String16 MakeIndexString(unsigned int index) {
- std::string str("[");
- str.append(std::to_string(index));
- str.append("]");
- return String16(str.c_str());
-}
-
} // namespace
namespace android {
@@ -237,23 +81,6 @@
}
}
-void MediaDrmMetrics::Export(PersistableBundle *metrics) {
- if (!metrics) {
- ALOGE("metrics was unexpectedly null.");
- return;
- }
- ExportCounterMetric(mOpenSessionCounter, metrics);
- ExportCounterMetric(mCloseSessionCounter, metrics);
- ExportEventMetric(mGetKeyRequestTimeUs, metrics);
- ExportEventMetric(mProvideKeyResponseTimeUs, metrics);
- ExportCounterMetric(mGetProvisionRequestCounter, metrics);
- ExportCounterMetric(mProvideProvisionResponseCounter, metrics);
- ExportCounterMetricWithAttributeNames(mKeyStatusChangeCounter, metrics);
- ExportCounterMetricWithAttributeNames(mEventCounter, metrics);
- ExportCounterMetric(mGetDeviceUniqueIdCounter, metrics);
- ExportSessionLifespans(mSessionLifespans, metrics);
-}
-
status_t MediaDrmMetrics::GetSerializedMetrics(std::string *serializedMetrics) {
if (!serializedMetrics) {
@@ -361,62 +188,14 @@
return OK;
}
+std::map<std::string, std::pair<int64_t, int64_t>> MediaDrmMetrics::GetSessionLifespans() const {
+ return mSessionLifespans;
+}
+
int64_t MediaDrmMetrics::GetCurrentTimeMs() {
struct timeval tv;
gettimeofday(&tv, NULL);
return ((int64_t)tv.tv_sec * 1000) + ((int64_t)tv.tv_usec / 1000);
}
-status_t MediaDrmMetrics::HidlMetricsToBundle(
- const hidl_vec<DrmMetricGroup> &hidlMetricGroups,
- PersistableBundle *bundleMetricGroups) {
- if (bundleMetricGroups == nullptr) {
- return UNEXPECTED_NULL;
- }
- if (hidlMetricGroups.size() == 0) {
- return OK;
- }
-
- int groupIndex = 0;
- std::map<String16, int> indexMap;
- for (const auto &hidlMetricGroup : hidlMetricGroups) {
- PersistableBundle bundleMetricGroup;
- for (const auto &hidlMetric : hidlMetricGroup.metrics) {
- String16 metricName(hidlMetric.name.c_str());
- PersistableBundle bundleMetric;
- // Add metric component values.
- for (const auto &value : hidlMetric.values) {
- SetValue(String16(value.componentName.c_str()), value.type,
- value, &bundleMetric);
- }
- // Set metric attributes.
- PersistableBundle bundleMetricAttributes;
- for (const auto &attribute : hidlMetric.attributes) {
- SetValue(String16(attribute.name.c_str()), attribute.type,
- attribute, &bundleMetricAttributes);
- }
- // Add attributes to the bundle metric.
- bundleMetric.putPersistableBundle(String16("attributes"),
- bundleMetricAttributes);
- // Add one layer of indirection, allowing for repeated metric names.
- PersistableBundle repeatedMetrics;
- bundleMetricGroup.getPersistableBundle(metricName,
- &repeatedMetrics);
- int index = indexMap[metricName];
- repeatedMetrics.putPersistableBundle(MakeIndexString(index),
- bundleMetric);
- indexMap[metricName] = ++index;
-
- // Add the bundle metric to the group of metrics.
- bundleMetricGroup.putPersistableBundle(metricName,
- repeatedMetrics);
- }
- // Add the bundle metric group to the collection of groups.
- bundleMetricGroups->putPersistableBundle(MakeIndexString(groupIndex++),
- bundleMetricGroup);
- }
-
- return OK;
-}
-
} // namespace android
diff --git a/drm/libmediadrm/DrmMetricsConsumer.cpp b/drm/libmediadrm/DrmMetricsConsumer.cpp
new file mode 100644
index 0000000..b47b4ff
--- /dev/null
+++ b/drm/libmediadrm/DrmMetricsConsumer.cpp
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 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_TAG "DrmMetricsConsumer"
+
+#include <android-base/macros.h>
+#include <mediadrm/DrmMetricsConsumer.h>
+#include <mediadrm/DrmMetrics.h>
+#include <utils/String8.h>
+#include <utils/String16.h>
+
+using ::android::String16;
+using ::android::String8;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::drm::V1_0::EventType;
+using ::android::hardware::drm::V1_2::KeyStatusType;
+using ::android::hardware::drm::V1_1::DrmMetricGroup;
+using ::android::os::PersistableBundle;
+
+namespace {
+
+template <typename T> std::string GetAttributeName(T type);
+
+template <> std::string GetAttributeName<KeyStatusType>(KeyStatusType type) {
+ static const char *type_names[] = {"USABLE", "EXPIRED",
+ "OUTPUT_NOT_ALLOWED", "STATUS_PENDING",
+ "INTERNAL_ERROR"};
+ if (((size_t)type) > arraysize(type_names)) {
+ return "UNKNOWN_TYPE";
+ }
+ return type_names[(size_t)type];
+}
+
+template <> std::string GetAttributeName<EventType>(EventType type) {
+ static const char *type_names[] = {"PROVISION_REQUIRED", "KEY_NEEDED",
+ "KEY_EXPIRED", "VENDOR_DEFINED",
+ "SESSION_RECLAIMED"};
+ if (((size_t)type) > arraysize(type_names)) {
+ return "UNKNOWN_TYPE";
+ }
+ return type_names[(size_t)type];
+}
+
+template <typename T>
+void ExportCounterMetric(const android::CounterMetric<T> &counter,
+ PersistableBundle *metrics) {
+ if (!metrics) {
+ ALOGE("metrics was unexpectedly null.");
+ return;
+ }
+ std::string success_count_name = counter.metric_name() + ".ok.count";
+ std::string error_count_name = counter.metric_name() + ".error.count";
+ std::vector<int64_t> status_values;
+ counter.ExportValues(
+ [&](const android::status_t status, const int64_t value) {
+ if (status == android::OK) {
+ metrics->putLong(android::String16(success_count_name.c_str()),
+ value);
+ } else {
+ int64_t total_errors(0);
+ metrics->getLong(android::String16(error_count_name.c_str()),
+ &total_errors);
+ metrics->putLong(android::String16(error_count_name.c_str()),
+ total_errors + value);
+ status_values.push_back(status);
+ }
+ });
+ if (!status_values.empty()) {
+ std::string error_list_name = counter.metric_name() + ".error.list";
+ metrics->putLongVector(android::String16(error_list_name.c_str()),
+ status_values);
+ }
+}
+
+template <typename T>
+void ExportCounterMetricWithAttributeNames(
+ const android::CounterMetric<T> &counter, PersistableBundle *metrics) {
+ if (!metrics) {
+ ALOGE("metrics was unexpectedly null.");
+ return;
+ }
+ counter.ExportValues([&](const T &attribute, const int64_t value) {
+ std::string name = counter.metric_name() + "." +
+ GetAttributeName(attribute) + ".count";
+ metrics->putLong(android::String16(name.c_str()), value);
+ });
+}
+
+template <typename T>
+void ExportEventMetric(const android::EventMetric<T> &event,
+ PersistableBundle *metrics) {
+ if (!metrics) {
+ ALOGE("metrics was unexpectedly null.");
+ return;
+ }
+ std::string success_count_name = event.metric_name() + ".ok.count";
+ std::string error_count_name = event.metric_name() + ".error.count";
+ std::string timing_name = event.metric_name() + ".ok.average_time_micros";
+ std::vector<int64_t> status_values;
+ event.ExportValues([&](const android::status_t &status,
+ const android::EventStatistics &value) {
+ if (status == android::OK) {
+ metrics->putLong(android::String16(success_count_name.c_str()),
+ value.count);
+ metrics->putLong(android::String16(timing_name.c_str()),
+ value.mean);
+ } else {
+ int64_t total_errors(0);
+ metrics->getLong(android::String16(error_count_name.c_str()),
+ &total_errors);
+ metrics->putLong(android::String16(error_count_name.c_str()),
+ total_errors + value.count);
+ status_values.push_back(status);
+ }
+ });
+ if (!status_values.empty()) {
+ std::string error_list_name = event.metric_name() + ".error.list";
+ metrics->putLongVector(android::String16(error_list_name.c_str()),
+ status_values);
+ }
+}
+
+void ExportSessionLifespans(
+ const std::map<std::string, std::pair<int64_t, int64_t>> &sessionLifespans,
+ PersistableBundle *metrics) {
+ if (!metrics) {
+ ALOGE("metrics was unexpectedly null.");
+ return;
+ }
+
+ if (sessionLifespans.empty()) {
+ return;
+ }
+
+ PersistableBundle startTimesBundle;
+ PersistableBundle endTimesBundle;
+ for (auto it = sessionLifespans.begin(); it != sessionLifespans.end();
+ it++) {
+ String16 key(it->first.c_str(), it->first.size());
+ startTimesBundle.putLong(key, it->second.first);
+ endTimesBundle.putLong(key, it->second.second);
+ }
+ metrics->putPersistableBundle(
+ android::String16("drm.mediadrm.session_start_times_ms"),
+ startTimesBundle);
+ metrics->putPersistableBundle(
+ android::String16("drm.mediadrm.session_end_times_ms"), endTimesBundle);
+}
+
+template <typename CT>
+void SetValue(const String16 &name, DrmMetricGroup::ValueType type,
+ const CT &value, PersistableBundle *bundle) {
+ switch (type) {
+ case DrmMetricGroup::ValueType::INT64_TYPE:
+ bundle->putLong(name, value.int64Value);
+ break;
+ case DrmMetricGroup::ValueType::DOUBLE_TYPE:
+ bundle->putDouble(name, value.doubleValue);
+ break;
+ case DrmMetricGroup::ValueType::STRING_TYPE:
+ bundle->putString(name, String16(value.stringValue.c_str()));
+ break;
+ default:
+ ALOGE("Unexpected value type: %hhu", type);
+ }
+}
+
+inline String16 MakeIndexString(unsigned int index) {
+ std::string str("[");
+ str.append(std::to_string(index));
+ str.append("]");
+ return String16(str.c_str());
+}
+
+} // namespace
+
+namespace android {
+
+status_t DrmMetricsConsumer::consumeFrameworkMetrics(const MediaDrmMetrics &metrics) {
+ ExportCounterMetric(metrics.mOpenSessionCounter, mBundle);
+ ExportCounterMetric(metrics.mCloseSessionCounter, mBundle);
+ ExportEventMetric(metrics.mGetKeyRequestTimeUs, mBundle);
+ ExportEventMetric(metrics.mProvideKeyResponseTimeUs, mBundle);
+ ExportCounterMetric(metrics.mGetProvisionRequestCounter, mBundle);
+ ExportCounterMetric(metrics.mProvideProvisionResponseCounter, mBundle);
+ ExportCounterMetricWithAttributeNames(metrics.mKeyStatusChangeCounter, mBundle);
+ ExportCounterMetricWithAttributeNames(metrics.mEventCounter, mBundle);
+ ExportCounterMetric(metrics.mGetDeviceUniqueIdCounter, mBundle);
+ ExportSessionLifespans(metrics.GetSessionLifespans(), mBundle);
+ return android::OK;
+}
+
+status_t DrmMetricsConsumer::consumeHidlMetrics(
+ const String8 &vendor,
+ const hidl_vec<DrmMetricGroup> &pluginMetrics) {
+ PersistableBundle pluginBundle;
+ if (DrmMetricsConsumer::HidlMetricsToBundle(
+ pluginMetrics, &pluginBundle) == OK) {
+ mBundle->putPersistableBundle(String16(vendor), pluginBundle);
+ }
+ return android::OK;
+}
+
+status_t DrmMetricsConsumer::HidlMetricsToBundle(
+ const hidl_vec<DrmMetricGroup> &hidlMetricGroups,
+ PersistableBundle *bundleMetricGroups) {
+ if (bundleMetricGroups == nullptr) {
+ return UNEXPECTED_NULL;
+ }
+ if (hidlMetricGroups.size() == 0) {
+ return OK;
+ }
+
+ int groupIndex = 0;
+ std::map<String16, int> indexMap;
+ for (const auto &hidlMetricGroup : hidlMetricGroups) {
+ PersistableBundle bundleMetricGroup;
+ for (const auto &hidlMetric : hidlMetricGroup.metrics) {
+ String16 metricName(hidlMetric.name.c_str());
+ PersistableBundle bundleMetric;
+ // Add metric component values.
+ for (const auto &value : hidlMetric.values) {
+ SetValue(String16(value.componentName.c_str()), value.type,
+ value, &bundleMetric);
+ }
+ // Set metric attributes.
+ PersistableBundle bundleMetricAttributes;
+ for (const auto &attribute : hidlMetric.attributes) {
+ SetValue(String16(attribute.name.c_str()), attribute.type,
+ attribute, &bundleMetricAttributes);
+ }
+ // Add attributes to the bundle metric.
+ bundleMetric.putPersistableBundle(String16("attributes"),
+ bundleMetricAttributes);
+ // Add one layer of indirection, allowing for repeated metric names.
+ PersistableBundle repeatedMetrics;
+ bundleMetricGroup.getPersistableBundle(metricName,
+ &repeatedMetrics);
+ int index = indexMap[metricName];
+ repeatedMetrics.putPersistableBundle(MakeIndexString(index),
+ bundleMetric);
+ indexMap[metricName] = ++index;
+
+ // Add the bundle metric to the group of metrics.
+ bundleMetricGroup.putPersistableBundle(metricName,
+ repeatedMetrics);
+ }
+ // Add the bundle metric group to the collection of groups.
+ bundleMetricGroups->putPersistableBundle(MakeIndexString(groupIndex++),
+ bundleMetricGroup);
+ }
+
+ return OK;
+}
+
+} // namespace android
+
diff --git a/drm/libmediadrm/DrmSessionManager.cpp b/drm/libmediadrm/DrmSessionManager.cpp
index 0b927ef..7a4e1ae 100644
--- a/drm/libmediadrm/DrmSessionManager.cpp
+++ b/drm/libmediadrm/DrmSessionManager.cpp
@@ -18,22 +18,32 @@
#define LOG_TAG "DrmSessionManager"
#include <utils/Log.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IProcessInfoService.h>
-#include <binder/IServiceManager.h>
+#include <aidl/android/media/IResourceManagerClient.h>
+#include <aidl/android/media/IResourceManagerService.h>
+#include <aidl/android/media/MediaResourceParcel.h>
+#include <android/binder_ibinder.h>
+#include <android/binder_manager.h>
#include <cutils/properties.h>
-#include <media/IResourceManagerClient.h>
-#include <media/MediaResource.h>
+#include <mediadrm/DrmUtils.h>
#include <mediadrm/DrmSessionManager.h>
#include <unistd.h>
#include <utils/String8.h>
#include <vector>
-#include "ResourceManagerService.h"
-
namespace android {
+using aidl::android::media::MediaResourceParcel;
+
+namespace {
+void ResourceManagerServiceDied(void* cookie) {
+ auto thiz = static_cast<DrmSessionManager*>(cookie);
+ thiz->binderDied();
+}
+}
+
+using ::ndk::ScopedAStatus;
+
static String8 GetSessionIdString(const Vector<uint8_t> &sessionId) {
String8 sessionIdStr;
for (size_t i = 0; i < sessionId.size(); ++i) {
@@ -42,33 +52,28 @@
return sessionIdStr;
}
-static std::vector<uint8_t> toStdVec(const Vector<uint8_t> &vector) {
- const uint8_t *v = vector.array();
- std::vector<uint8_t> vec(v, v + vector.size());
+template <typename Byte = uint8_t>
+static std::vector<Byte> toStdVec(const Vector<uint8_t> &vector) {
+ auto v = reinterpret_cast<const Byte *>(vector.array());
+ std::vector<Byte> vec(v, v + vector.size());
return vec;
}
-static uint64_t toClientId(const sp<IResourceManagerClient>& drm) {
- return reinterpret_cast<int64_t>(drm.get());
-}
-
-static Vector<MediaResource> toResourceVec(const Vector<uint8_t> &sessionId) {
- Vector<MediaResource> resources;
- // use UINT64_MAX to decrement through addition overflow
- resources.push_back(MediaResource(MediaResource::kDrmSession, toStdVec(sessionId), UINT64_MAX));
+static std::vector<MediaResourceParcel> toResourceVec(
+ const Vector<uint8_t> &sessionId, int64_t value) {
+ using Type = aidl::android::media::MediaResourceType;
+ using SubType = aidl::android::media::MediaResourceSubType;
+ std::vector<MediaResourceParcel> resources;
+ MediaResourceParcel resource{
+ Type::kDrmSession, SubType::kUnspecifiedSubType,
+ toStdVec<int8_t>(sessionId), value};
+ resources.push_back(resource);
return resources;
}
-static sp<IResourceManagerService> getResourceManagerService() {
- if (property_get_bool("persist.device_config.media_native.mediadrmserver", 1)) {
- return new ResourceManagerService();
- }
- sp<IServiceManager> sm = defaultServiceManager();
- if (sm == NULL) {
- return NULL;
- }
- sp<IBinder> binder = sm->getService(String16("media.resource_manager"));
- return interface_cast<IResourceManagerService>(binder);
+static std::shared_ptr<IResourceManagerService> getResourceManagerService() {
+ ::ndk::SpAIBinder binder(AServiceManager_getService("media.resource_manager"));
+ return IResourceManagerService::fromBinder(binder);
}
bool isEqualSessionId(const Vector<uint8_t> &sessionId1, const Vector<uint8_t> &sessionId2) {
@@ -84,7 +89,7 @@
}
sp<DrmSessionManager> DrmSessionManager::Instance() {
- static sp<DrmSessionManager> drmSessionManager = new DrmSessionManager();
+ auto drmSessionManager = new DrmSessionManager();
drmSessionManager->init();
return drmSessionManager;
}
@@ -93,9 +98,10 @@
: DrmSessionManager(getResourceManagerService()) {
}
-DrmSessionManager::DrmSessionManager(const sp<IResourceManagerService> &service)
+DrmSessionManager::DrmSessionManager(const std::shared_ptr<IResourceManagerService> &service)
: mService(service),
- mInitialized(false) {
+ mInitialized(false),
+ mDeathRecipient(AIBinder_DeathRecipient_new(ResourceManagerServiceDied)) {
if (mService == NULL) {
ALOGE("Failed to init ResourceManagerService");
}
@@ -103,7 +109,7 @@
DrmSessionManager::~DrmSessionManager() {
if (mService != NULL) {
- IInterface::asBinder(mService)->unlinkToDeath(this);
+ AIBinder_unlinkToDeath(mService->asBinder().get(), mDeathRecipient.get(), this);
}
}
@@ -114,13 +120,13 @@
}
mInitialized = true;
if (mService != NULL) {
- IInterface::asBinder(mService)->linkToDeath(this);
+ AIBinder_linkToDeath(mService->asBinder().get(), mDeathRecipient.get(), this);
}
}
void DrmSessionManager::addSession(int pid,
- const sp<IResourceManagerClient>& drm, const Vector<uint8_t> &sessionId) {
- uid_t uid = IPCThreadState::self()->getCallingUid();
+ const std::shared_ptr<IResourceManagerClient>& drm, const Vector<uint8_t> &sessionId) {
+ uid_t uid = AIBinder_getCallingUid();
ALOGV("addSession(pid %d, uid %d, drm %p, sessionId %s)", pid, uid, drm.get(),
GetSessionIdString(sessionId).string());
@@ -129,9 +135,9 @@
return;
}
- int64_t clientId = toClientId(drm);
+ static int64_t clientId = 0;
mSessionMap[toStdVec(sessionId)] = (SessionInfo){pid, uid, clientId};
- mService->addResource(pid, uid, clientId, drm, toResourceVec(sessionId));
+ mService->addResource(pid, uid, clientId++, drm, toResourceVec(sessionId, INT64_MAX));
}
void DrmSessionManager::useSession(const Vector<uint8_t> &sessionId) {
@@ -144,7 +150,7 @@
}
auto info = it->second;
- mService->addResource(info.pid, info.uid, info.clientId, NULL, toResourceVec(sessionId));
+ mService->addResource(info.pid, info.uid, info.clientId, NULL, toResourceVec(sessionId, -1));
}
void DrmSessionManager::removeSession(const Vector<uint8_t> &sessionId) {
@@ -157,7 +163,7 @@
}
auto info = it->second;
- mService->removeResource(info.pid, info.clientId, toResourceVec(sessionId));
+ mService->removeResource(info.pid, info.clientId, toResourceVec(sessionId, INT64_MAX));
mSessionMap.erase(it);
}
@@ -166,7 +172,7 @@
// unlock early because reclaimResource might callback into removeSession
mLock.lock();
- sp<IResourceManagerService> service(mService);
+ std::shared_ptr<IResourceManagerService> service(mService);
mLock.unlock();
if (service == NULL) {
@@ -176,7 +182,9 @@
// cannot update mSessionMap because we do not know which sessionId is reclaimed;
// we rely on IResourceManagerClient to removeSession in reclaimResource
Vector<uint8_t> dummy;
- return service->reclaimResource(callingPid, toResourceVec(dummy));
+ bool success;
+ ScopedAStatus status = service->reclaimResource(callingPid, toResourceVec(dummy, INT64_MAX), &success);
+ return status.isOk() && success;
}
size_t DrmSessionManager::getSessionCount() const {
@@ -189,10 +197,10 @@
return mSessionMap.count(toStdVec(sessionId));
}
-void DrmSessionManager::binderDied(const wp<IBinder>& /*who*/) {
+void DrmSessionManager::binderDied() {
ALOGW("ResourceManagerService died.");
Mutex::Autolock lock(mLock);
- mService.clear();
+ mService.reset();
}
} // namespace android
diff --git a/drm/libmediadrm/DrmUtils.cpp b/drm/libmediadrm/DrmUtils.cpp
new file mode 100644
index 0000000..3549637
--- /dev/null
+++ b/drm/libmediadrm/DrmUtils.cpp
@@ -0,0 +1,177 @@
+/*
+ * 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 "DrmUtils"
+
+#include <android/hardware/drm/1.0/ICryptoFactory.h>
+#include <android/hardware/drm/1.0/ICryptoPlugin.h>
+#include <android/hardware/drm/1.0/IDrmFactory.h>
+#include <android/hardware/drm/1.0/IDrmPlugin.h>
+#include <android/hardware/drm/1.1/ICryptoFactory.h>
+#include <android/hardware/drm/1.1/IDrmFactory.h>
+#include <android/hardware/drm/1.2/ICryptoFactory.h>
+#include <android/hardware/drm/1.2/IDrmFactory.h>
+#include <android/hidl/manager/1.0/IServiceManager.h>
+#include <hidl/HidlSupport.h>
+
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <utils/String16.h>
+#include <cutils/properties.h>
+
+#include <mediadrm/CryptoHal.h>
+#include <mediadrm/DrmHal.h>
+#include <mediadrm/DrmUtils.h>
+#include <mediadrm/ICrypto.h>
+#include <mediadrm/IDrm.h>
+
+using HServiceManager = ::android::hidl::manager::V1_0::IServiceManager;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using namespace ::android::hardware::drm;
+
+namespace android {
+namespace DrmUtils {
+
+namespace {
+
+template<typename Hal>
+Hal *MakeObject(status_t *pstatus) {
+ status_t err = OK;
+ status_t &status = pstatus ? *pstatus : err;
+ auto obj = new Hal();
+ status = obj->initCheck();
+ if (status != OK && status != NO_INIT) {
+ return NULL;
+ }
+ return obj;
+}
+
+template <typename Hal, typename V>
+void MakeHidlFactories(const uint8_t uuid[16], V &factories) {
+ sp<HServiceManager> serviceManager = HServiceManager::getService();
+ if (serviceManager == nullptr) {
+ ALOGE("Failed to get service manager");
+ exit(-1);
+ }
+
+ serviceManager->listByInterface(Hal::descriptor, [&](const hidl_vec<hidl_string> ®istered) {
+ for (const auto &instance : registered) {
+ auto factory = Hal::getService(instance);
+ if (factory != nullptr) {
+ ALOGI("found %s %s", Hal::descriptor, instance.c_str());
+ if (!uuid || factory->isCryptoSchemeSupported(uuid)) {
+ factories.push_back(factory);
+ }
+ }
+ }
+ });
+}
+
+hidl_vec<uint8_t> toHidlVec(const void *ptr, size_t size) {
+ hidl_vec<uint8_t> vec(size);
+ if (ptr != nullptr) {
+ memcpy(vec.data(), ptr, size);
+ }
+ return vec;
+}
+
+hidl_array<uint8_t, 16> toHidlArray16(const uint8_t *ptr) {
+ if (ptr == nullptr) {
+ return hidl_array<uint8_t, 16>();
+ }
+ return hidl_array<uint8_t, 16>(ptr);
+}
+
+sp<::V1_0::IDrmPlugin> MakeDrmPlugin(const sp<::V1_0::IDrmFactory> &factory,
+ const uint8_t uuid[16], const char *appPackageName) {
+ sp<::V1_0::IDrmPlugin> plugin;
+ factory->createPlugin(toHidlArray16(uuid), hidl_string(appPackageName),
+ [&](::V1_0::Status status, const sp<::V1_0::IDrmPlugin> &hPlugin) {
+ if (status != ::V1_0::Status::OK) {
+ return;
+ }
+ plugin = hPlugin;
+ });
+ return plugin;
+}
+
+sp<::V1_0::ICryptoPlugin> MakeCryptoPlugin(const sp<::V1_0::ICryptoFactory> &factory,
+ const uint8_t uuid[16], const void *initData,
+ size_t initDataSize) {
+ sp<::V1_0::ICryptoPlugin> plugin;
+ factory->createPlugin(toHidlArray16(uuid), toHidlVec(initData, initDataSize),
+ [&](::V1_0::Status status, const sp<::V1_0::ICryptoPlugin> &hPlugin) {
+ if (status != ::V1_0::Status::OK) {
+ return;
+ }
+ plugin = hPlugin;
+ });
+ return plugin;
+}
+
+} // namespace
+
+bool UseDrmService() {
+ return property_get_bool("mediadrm.use_mediadrmserver", true);
+}
+
+sp<IDrm> MakeDrm(status_t *pstatus) {
+ return MakeObject<DrmHal>(pstatus);
+}
+
+sp<ICrypto> MakeCrypto(status_t *pstatus) {
+ return MakeObject<CryptoHal>(pstatus);
+}
+
+std::vector<sp<::V1_0::IDrmFactory>> MakeDrmFactories(const uint8_t uuid[16]) {
+ std::vector<sp<::V1_0::IDrmFactory>> drmFactories;
+ MakeHidlFactories<::V1_0::IDrmFactory>(uuid, drmFactories);
+ MakeHidlFactories<::V1_1::IDrmFactory>(uuid, drmFactories);
+ MakeHidlFactories<::V1_2::IDrmFactory>(uuid, drmFactories);
+ return drmFactories;
+}
+
+std::vector<sp<::V1_0::IDrmPlugin>> MakeDrmPlugins(const uint8_t uuid[16],
+ const char *appPackageName) {
+ std::vector<sp<::V1_0::IDrmPlugin>> plugins;
+ for (const auto &factory : MakeDrmFactories(uuid)) {
+ plugins.push_back(MakeDrmPlugin(factory, uuid, appPackageName));
+ }
+ return plugins;
+}
+
+std::vector<sp<::V1_0::ICryptoFactory>> MakeCryptoFactories(const uint8_t uuid[16]) {
+ std::vector<sp<::V1_0::ICryptoFactory>> cryptoFactories;
+ MakeHidlFactories<::V1_0::ICryptoFactory>(uuid, cryptoFactories);
+ MakeHidlFactories<::V1_1::ICryptoFactory>(uuid, cryptoFactories);
+ MakeHidlFactories<::V1_2::ICryptoFactory>(uuid, cryptoFactories);
+ return cryptoFactories;
+}
+
+std::vector<sp<ICryptoPlugin>> MakeCryptoPlugins(const uint8_t uuid[16], const void *initData,
+ size_t initDataSize) {
+ std::vector<sp<ICryptoPlugin>> plugins;
+ for (const auto &factory : MakeCryptoFactories(uuid)) {
+ plugins.push_back(MakeCryptoPlugin(factory, uuid, initData, initDataSize));
+ }
+ return plugins;
+}
+
+} // namespace DrmUtils
+} // namespace android
diff --git a/drm/libmediadrm/ICrypto.cpp b/drm/libmediadrm/ICrypto.cpp
deleted file mode 100644
index a2594aa..0000000
--- a/drm/libmediadrm/ICrypto.cpp
+++ /dev/null
@@ -1,475 +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 "ICrypto"
-#include <binder/Parcel.h>
-#include <binder/IMemory.h>
-#include <cutils/log.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AString.h>
-#include <mediadrm/ICrypto.h>
-#include <utils/Log.h>
-
-namespace android {
-
-enum {
- INIT_CHECK = IBinder::FIRST_CALL_TRANSACTION,
- IS_CRYPTO_SUPPORTED,
- CREATE_PLUGIN,
- DESTROY_PLUGIN,
- REQUIRES_SECURE_COMPONENT,
- DECRYPT,
- NOTIFY_RESOLUTION,
- SET_MEDIADRM_SESSION,
- SET_HEAP,
- UNSET_HEAP,
-};
-
-struct BpCrypto : public BpInterface<ICrypto> {
- explicit BpCrypto(const sp<IBinder> &impl)
- : BpInterface<ICrypto>(impl) {
- }
-
- virtual status_t initCheck() const {
- Parcel data, reply;
- data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
- remote()->transact(INIT_CHECK, data, &reply);
-
- return reply.readInt32();
- }
-
- virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]) {
- Parcel data, reply;
- data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
- data.write(uuid, 16);
- remote()->transact(IS_CRYPTO_SUPPORTED, data, &reply);
-
- return reply.readInt32() != 0;
- }
-
- virtual status_t createPlugin(
- const uint8_t uuid[16], const void *opaqueData, size_t opaqueSize) {
- Parcel data, reply;
- data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
- data.write(uuid, 16);
- data.writeInt32(opaqueSize);
-
- if (opaqueSize > 0) {
- data.write(opaqueData, opaqueSize);
- }
-
- remote()->transact(CREATE_PLUGIN, data, &reply);
-
- return reply.readInt32();
- }
-
- virtual status_t destroyPlugin() {
- Parcel data, reply;
- data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
- remote()->transact(DESTROY_PLUGIN, data, &reply);
-
- return reply.readInt32();
- }
-
- virtual bool requiresSecureDecoderComponent(
- const char *mime) const {
- Parcel data, reply;
- data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
- data.writeCString(mime);
- remote()->transact(REQUIRES_SECURE_COMPONENT, data, &reply);
-
- return reply.readInt32() != 0;
- }
-
- virtual ssize_t decrypt(const uint8_t key[16], const uint8_t iv[16],
- CryptoPlugin::Mode mode, const CryptoPlugin::Pattern &pattern,
- const SourceBuffer &source, size_t offset,
- const CryptoPlugin::SubSample *subSamples, size_t numSubSamples,
- const DestinationBuffer &destination, AString *errorDetailMsg) {
- Parcel data, reply;
- data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
- data.writeInt32(mode);
- data.writeInt32(pattern.mEncryptBlocks);
- data.writeInt32(pattern.mSkipBlocks);
-
- static const uint8_t kDummy[16] = { 0 };
-
- if (key == NULL) {
- key = kDummy;
- }
-
- if (iv == NULL) {
- iv = kDummy;
- }
-
- data.write(key, 16);
- data.write(iv, 16);
-
- size_t totalSize = 0;
- for (size_t i = 0; i < numSubSamples; ++i) {
- totalSize += subSamples[i].mNumBytesOfEncryptedData;
- totalSize += subSamples[i].mNumBytesOfClearData;
- }
-
- data.writeInt32(totalSize);
- data.writeStrongBinder(IInterface::asBinder(source.mSharedMemory));
- data.writeInt32(source.mHeapSeqNum);
- data.writeInt32(offset);
-
- data.writeInt32(numSubSamples);
- data.write(subSamples, sizeof(CryptoPlugin::SubSample) * numSubSamples);
-
- data.writeInt32((int32_t)destination.mType);
- if (destination.mType == kDestinationTypeNativeHandle) {
- if (destination.mHandle == NULL) {
- return BAD_VALUE;
- }
- data.writeNativeHandle(destination.mHandle);
- } else {
- if (destination.mSharedMemory == NULL) {
- return BAD_VALUE;
- }
- data.writeStrongBinder(IInterface::asBinder(destination.mSharedMemory));
- }
-
- remote()->transact(DECRYPT, data, &reply);
-
- ssize_t result = reply.readInt32();
-
- if (isCryptoError(result)) {
- AString msg = reply.readCString();
- if (errorDetailMsg) {
- *errorDetailMsg = msg;
- }
- }
-
- return result;
- }
-
- virtual void notifyResolution(
- uint32_t width, uint32_t height) {
- Parcel data, reply;
- data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
- data.writeInt32(width);
- data.writeInt32(height);
- remote()->transact(NOTIFY_RESOLUTION, data, &reply);
- }
-
- virtual status_t setMediaDrmSession(const Vector<uint8_t> &sessionId) {
- Parcel data, reply;
- data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
-
- writeVector(data, sessionId);
- remote()->transact(SET_MEDIADRM_SESSION, data, &reply);
-
- return reply.readInt32();
- }
-
- virtual int32_t setHeap(const sp<IMemoryHeap> &heap) {
- Parcel data, reply;
- data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
- data.writeStrongBinder(IInterface::asBinder(heap));
- status_t err = remote()->transact(SET_HEAP, data, &reply);
- if (err != NO_ERROR) {
- return -1;
- }
- int32_t seqNum;
- if (reply.readInt32(&seqNum) != NO_ERROR) {
- return -1;
- }
- return seqNum;
- }
-
- virtual void unsetHeap(int32_t seqNum) {
- Parcel data, reply;
- data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
- data.writeInt32(seqNum);
- remote()->transact(UNSET_HEAP, data, &reply);
- return;
- }
-
-
-private:
- void readVector(Parcel &reply, Vector<uint8_t> &vector) const {
- uint32_t size = reply.readInt32();
- vector.insertAt((size_t)0, size);
- reply.read(vector.editArray(), size);
- }
-
- void writeVector(Parcel &data, Vector<uint8_t> const &vector) const {
- data.writeInt32(vector.size());
- data.write(vector.array(), vector.size());
- }
-
- DISALLOW_EVIL_CONSTRUCTORS(BpCrypto);
-};
-
-IMPLEMENT_META_INTERFACE(Crypto, "android.hardware.ICrypto");
-
-////////////////////////////////////////////////////////////////////////////////
-
-void BnCrypto::readVector(const Parcel &data, Vector<uint8_t> &vector) const {
- uint32_t size = data.readInt32();
- if (vector.insertAt((size_t)0, size) < 0) {
- vector.clear();
- }
- if (data.read(vector.editArray(), size) != NO_ERROR) {
- vector.clear();
- android_errorWriteWithInfoLog(0x534e4554, "62872384", -1, NULL, 0);
- }
-}
-
-void BnCrypto::writeVector(Parcel *reply, Vector<uint8_t> const &vector) const {
- reply->writeInt32(vector.size());
- reply->write(vector.array(), vector.size());
-}
-
-status_t BnCrypto::onTransact(
- uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
- switch (code) {
- case INIT_CHECK:
- {
- CHECK_INTERFACE(ICrypto, data, reply);
- reply->writeInt32(initCheck());
-
- return OK;
- }
-
- case IS_CRYPTO_SUPPORTED:
- {
- CHECK_INTERFACE(ICrypto, data, reply);
- uint8_t uuid[16];
- data.read(uuid, sizeof(uuid));
- reply->writeInt32(isCryptoSchemeSupported(uuid));
-
- return OK;
- }
-
- case CREATE_PLUGIN:
- {
- CHECK_INTERFACE(ICrypto, data, reply);
-
- uint8_t uuid[16];
- data.read(uuid, sizeof(uuid));
-
- size_t opaqueSize = data.readInt32();
- void *opaqueData = NULL;
-
- const size_t kMaxOpaqueSize = 100 * 1024;
- if (opaqueSize > kMaxOpaqueSize) {
- return BAD_VALUE;
- }
-
- opaqueData = malloc(opaqueSize);
- if (NULL == opaqueData) {
- return NO_MEMORY;
- }
-
- data.read(opaqueData, opaqueSize);
- reply->writeInt32(createPlugin(uuid, opaqueData, opaqueSize));
-
- free(opaqueData);
- opaqueData = NULL;
-
- return OK;
- }
-
- case DESTROY_PLUGIN:
- {
- CHECK_INTERFACE(ICrypto, data, reply);
- reply->writeInt32(destroyPlugin());
-
- return OK;
- }
-
- case REQUIRES_SECURE_COMPONENT:
- {
- CHECK_INTERFACE(ICrypto, data, reply);
-
- const char *mime = data.readCString();
- if (mime == NULL) {
- reply->writeInt32(BAD_VALUE);
- } else {
- reply->writeInt32(requiresSecureDecoderComponent(mime));
- }
-
- return OK;
- }
-
- case DECRYPT:
- {
- CHECK_INTERFACE(ICrypto, data, reply);
-
- CryptoPlugin::Mode mode = (CryptoPlugin::Mode)data.readInt32();
- CryptoPlugin::Pattern pattern;
- pattern.mEncryptBlocks = data.readInt32();
- pattern.mSkipBlocks = data.readInt32();
-
- uint8_t key[16];
- data.read(key, sizeof(key));
-
- uint8_t iv[16];
- data.read(iv, sizeof(iv));
-
- size_t totalSize = data.readInt32();
-
- SourceBuffer source;
-
- source.mSharedMemory =
- interface_cast<IMemory>(data.readStrongBinder());
- if (source.mSharedMemory == NULL) {
- reply->writeInt32(BAD_VALUE);
- return OK;
- }
- source.mHeapSeqNum = data.readInt32();
-
- int32_t offset = data.readInt32();
-
- int32_t numSubSamples = data.readInt32();
- if (numSubSamples < 0 || numSubSamples > 0xffff) {
- reply->writeInt32(BAD_VALUE);
- return OK;
- }
-
- std::unique_ptr<CryptoPlugin::SubSample[]> subSamples =
- std::make_unique<CryptoPlugin::SubSample[]>(numSubSamples);
-
- data.read(subSamples.get(),
- sizeof(CryptoPlugin::SubSample) * numSubSamples);
-
- DestinationBuffer destination;
- destination.mType = (DestinationType)data.readInt32();
- if (destination.mType == kDestinationTypeNativeHandle) {
- destination.mHandle = data.readNativeHandle();
- if (destination.mHandle == NULL) {
- reply->writeInt32(BAD_VALUE);
- return OK;
- }
- } else if (destination.mType == kDestinationTypeSharedMemory) {
- destination.mSharedMemory =
- interface_cast<IMemory>(data.readStrongBinder());
- if (destination.mSharedMemory == NULL) {
- reply->writeInt32(BAD_VALUE);
- return OK;
- }
- sp<IMemory> dest = destination.mSharedMemory;
- if (totalSize > dest->size() ||
- (size_t)dest->offset() > dest->size() - totalSize) {
- reply->writeInt32(BAD_VALUE);
- android_errorWriteLog(0x534e4554, "71389378");
- return OK;
- }
- } else {
- reply->writeInt32(BAD_VALUE);
- android_errorWriteLog(0x534e4554, "70526702");
- return OK;
- }
-
- AString errorDetailMsg;
- ssize_t result;
-
- size_t sumSubsampleSizes = 0;
- bool overflow = false;
- for (int32_t i = 0; i < numSubSamples; ++i) {
- CryptoPlugin::SubSample &ss = subSamples[i];
- if (sumSubsampleSizes <= SIZE_MAX - ss.mNumBytesOfEncryptedData) {
- sumSubsampleSizes += ss.mNumBytesOfEncryptedData;
- } else {
- overflow = true;
- }
- if (sumSubsampleSizes <= SIZE_MAX - ss.mNumBytesOfClearData) {
- sumSubsampleSizes += ss.mNumBytesOfClearData;
- } else {
- overflow = true;
- }
- }
-
- if (overflow || sumSubsampleSizes != totalSize) {
- result = -EINVAL;
- } else if (totalSize > source.mSharedMemory->size()) {
- result = -EINVAL;
- } else if ((size_t)offset > source.mSharedMemory->size() - totalSize) {
- result = -EINVAL;
- } else {
- result = decrypt(key, iv, mode, pattern, source, offset,
- subSamples.get(), numSubSamples, destination, &errorDetailMsg);
- }
-
- reply->writeInt32(result);
-
- if (isCryptoError(result)) {
- reply->writeCString(errorDetailMsg.c_str());
- }
-
- if (destination.mType == kDestinationTypeNativeHandle) {
- int err;
- if ((err = native_handle_close(destination.mHandle)) < 0) {
- ALOGW("secure buffer native_handle_close failed: %d", err);
- }
- if ((err = native_handle_delete(destination.mHandle)) < 0) {
- ALOGW("secure buffer native_handle_delete failed: %d", err);
- }
- }
-
- subSamples.reset();
- return OK;
- }
-
- case NOTIFY_RESOLUTION:
- {
- CHECK_INTERFACE(ICrypto, data, reply);
-
- int32_t width = data.readInt32();
- int32_t height = data.readInt32();
- notifyResolution(width, height);
-
- return OK;
- }
-
- case SET_MEDIADRM_SESSION:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- Vector<uint8_t> sessionId;
- readVector(data, sessionId);
- reply->writeInt32(setMediaDrmSession(sessionId));
- return OK;
- }
-
- case SET_HEAP:
- {
- CHECK_INTERFACE(ICrypto, data, reply);
- sp<IMemoryHeap> heap =
- interface_cast<IMemoryHeap>(data.readStrongBinder());
- reply->writeInt32(setHeap(heap));
- return OK;
- }
-
- case UNSET_HEAP:
- {
- CHECK_INTERFACE(ICrypto, data, reply);
- int32_t seqNum = data.readInt32();
- unsetHeap(seqNum);
- return OK;
- }
-
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-} // namespace android
diff --git a/drm/libmediadrm/IDrm.cpp b/drm/libmediadrm/IDrm.cpp
deleted file mode 100644
index 51274d1..0000000
--- a/drm/libmediadrm/IDrm.cpp
+++ /dev/null
@@ -1,1242 +0,0 @@
-/*
- * Copyright (C) 2013 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 "IDrm"
-#include <utils/Log.h>
-
-#include <binder/Parcel.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AString.h>
-#include <mediadrm/IDrm.h>
-
-namespace android {
-
-enum {
- INIT_CHECK = IBinder::FIRST_CALL_TRANSACTION,
- IS_CRYPTO_SUPPORTED,
- CREATE_PLUGIN,
- DESTROY_PLUGIN,
- OPEN_SESSION,
- CLOSE_SESSION,
- GET_KEY_REQUEST,
- PROVIDE_KEY_RESPONSE,
- REMOVE_KEYS,
- RESTORE_KEYS,
- QUERY_KEY_STATUS,
- GET_PROVISION_REQUEST,
- PROVIDE_PROVISION_RESPONSE,
- GET_SECURE_STOPS,
- RELEASE_SECURE_STOPS,
- GET_PROPERTY_STRING,
- GET_PROPERTY_BYTE_ARRAY,
- SET_PROPERTY_STRING,
- SET_PROPERTY_BYTE_ARRAY,
- GET_METRICS,
- SET_CIPHER_ALGORITHM,
- SET_MAC_ALGORITHM,
- ENCRYPT,
- DECRYPT,
- SIGN,
- SIGN_RSA,
- VERIFY,
- SET_LISTENER,
- GET_SECURE_STOP,
- REMOVE_ALL_SECURE_STOPS,
- GET_HDCP_LEVELS,
- GET_NUMBER_OF_SESSIONS,
- GET_SECURITY_LEVEL,
- REMOVE_SECURE_STOP,
- GET_SECURE_STOP_IDS,
- GET_OFFLINE_LICENSE_KEYSET_IDS,
- REMOVE_OFFLINE_LICENSE,
- GET_OFFLINE_LICENSE_STATE
-};
-
-struct BpDrm : public BpInterface<IDrm> {
- explicit BpDrm(const sp<IBinder> &impl)
- : BpInterface<IDrm>(impl) {
- }
-
- virtual status_t initCheck() const {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
- status_t status = remote()->transact(INIT_CHECK, data, &reply);
- if (status != OK) {
- return status;
- }
-
- return reply.readInt32();
- }
-
- virtual status_t isCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType,
- DrmPlugin::SecurityLevel level, bool *isSupported) {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
- data.write(uuid, 16);
- data.writeString8(mimeType);
- data.writeInt32(level);
-
- status_t status = remote()->transact(IS_CRYPTO_SUPPORTED, data, &reply);
- if (status != OK) {
- ALOGE("isCryptoSchemeSupported: binder call failed: %d", status);
- return status;
- }
- *isSupported = static_cast<bool>(reply.readInt32());
-
- return reply.readInt32();
- }
-
- virtual status_t createPlugin(const uint8_t uuid[16],
- const String8& appPackageName) {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
- data.write(uuid, 16);
- data.writeString8(appPackageName);
- status_t status = remote()->transact(CREATE_PLUGIN, data, &reply);
- if (status != OK) {
- ALOGE("createPlugin: binder call failed: %d", status);
- return status;
- }
-
- return reply.readInt32();
- }
-
- virtual status_t destroyPlugin() {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
- status_t status = remote()->transact(DESTROY_PLUGIN, data, &reply);
- if (status != OK) {
- return status;
- }
-
- return reply.readInt32();
- }
-
- virtual status_t openSession(DrmPlugin::SecurityLevel level,
- Vector<uint8_t> &sessionId) {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
- data.writeInt32(level);
-
- status_t status = remote()->transact(OPEN_SESSION, data, &reply);
- if (status != OK) {
- return status;
- }
- readVector(reply, sessionId);
-
- return reply.readInt32();
- }
-
- virtual status_t closeSession(Vector<uint8_t> const &sessionId) {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- writeVector(data, sessionId);
- status_t status = remote()->transact(CLOSE_SESSION, data, &reply);
- if (status != OK) {
- return status;
- }
-
- return reply.readInt32();
- }
-
- virtual status_t
- getKeyRequest(Vector<uint8_t> const &sessionId,
- Vector<uint8_t> const &initData,
- String8 const &mimeType, DrmPlugin::KeyType keyType,
- KeyedVector<String8, String8> const &optionalParameters,
- Vector<uint8_t> &request, String8 &defaultUrl,
- DrmPlugin::KeyRequestType *keyRequestType) {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- writeVector(data, sessionId);
- writeVector(data, initData);
- data.writeString8(mimeType);
- data.writeInt32((uint32_t)keyType);
-
- data.writeInt32(optionalParameters.size());
- for (size_t i = 0; i < optionalParameters.size(); ++i) {
- data.writeString8(optionalParameters.keyAt(i));
- data.writeString8(optionalParameters.valueAt(i));
- }
-
- status_t status = remote()->transact(GET_KEY_REQUEST, data, &reply);
- if (status != OK) {
- return status;
- }
-
- readVector(reply, request);
- defaultUrl = reply.readString8();
- *keyRequestType = static_cast<DrmPlugin::KeyRequestType>(reply.readInt32());
-
- return reply.readInt32();
- }
-
- virtual status_t provideKeyResponse(Vector<uint8_t> const &sessionId,
- Vector<uint8_t> const &response,
- Vector<uint8_t> &keySetId) {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
- writeVector(data, sessionId);
- writeVector(data, response);
-
- status_t status = remote()->transact(PROVIDE_KEY_RESPONSE, data, &reply);
- if (status != OK) {
- return status;
- }
-
- readVector(reply, keySetId);
-
- return reply.readInt32();
- }
-
- virtual status_t removeKeys(Vector<uint8_t> const &keySetId) {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- writeVector(data, keySetId);
- status_t status = remote()->transact(REMOVE_KEYS, data, &reply);
- if (status != OK) {
- return status;
- }
-
- return reply.readInt32();
- }
-
- virtual status_t restoreKeys(Vector<uint8_t> const &sessionId,
- Vector<uint8_t> const &keySetId) {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- writeVector(data, sessionId);
- writeVector(data, keySetId);
- status_t status = remote()->transact(RESTORE_KEYS, data, &reply);
- if (status != OK) {
- return status;
- }
-
- return reply.readInt32();
- }
-
- virtual status_t queryKeyStatus(Vector<uint8_t> const &sessionId,
- KeyedVector<String8, String8> &infoMap) const {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- writeVector(data, sessionId);
- status_t status = remote()->transact(QUERY_KEY_STATUS, data, &reply);
- if (status != OK) {
- return status;
- }
-
- infoMap.clear();
- size_t count = reply.readInt32();
- for (size_t i = 0; i < count; i++) {
- String8 key = reply.readString8();
- String8 value = reply.readString8();
- infoMap.add(key, value);
- }
- return reply.readInt32();
- }
-
- virtual status_t getProvisionRequest(String8 const &certType,
- String8 const &certAuthority,
- Vector<uint8_t> &request,
- String8 &defaultUrl) {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- data.writeString8(certType);
- data.writeString8(certAuthority);
- status_t status = remote()->transact(GET_PROVISION_REQUEST, data, &reply);
- if (status != OK) {
- return status;
- }
-
- readVector(reply, request);
- defaultUrl = reply.readString8();
-
- return reply.readInt32();
- }
-
- virtual status_t provideProvisionResponse(Vector<uint8_t> const &response,
- Vector<uint8_t> &certificate,
- Vector<uint8_t> &wrappedKey) {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- writeVector(data, response);
- status_t status = remote()->transact(PROVIDE_PROVISION_RESPONSE, data, &reply);
- if (status != OK) {
- return status;
- }
-
- readVector(reply, certificate);
- readVector(reply, wrappedKey);
-
- return reply.readInt32();
- }
-
- virtual status_t getSecureStops(List<Vector<uint8_t> > &secureStops) {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- status_t status = remote()->transact(GET_SECURE_STOPS, data, &reply);
- if (status != OK) {
- return status;
- }
-
- secureStops.clear();
- uint32_t count = reply.readInt32();
- for (size_t i = 0; i < count; i++) {
- Vector<uint8_t> secureStop;
- readVector(reply, secureStop);
- secureStops.push_back(secureStop);
- }
- return reply.readInt32();
- }
-
- virtual status_t getSecureStopIds(List<Vector<uint8_t> > &secureStopIds) {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- status_t status = remote()->transact(GET_SECURE_STOP_IDS, data, &reply);
- if (status != OK) {
- return status;
- }
-
- secureStopIds.clear();
- uint32_t count = reply.readInt32();
- for (size_t i = 0; i < count; i++) {
- Vector<uint8_t> secureStopId;
- readVector(reply, secureStopId);
- secureStopIds.push_back(secureStopId);
- }
- return reply.readInt32();
- }
-
- virtual status_t getSecureStop(Vector<uint8_t> const &ssid, Vector<uint8_t> &secureStop) {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- writeVector(data, ssid);
- status_t status = remote()->transact(GET_SECURE_STOP, data, &reply);
- if (status != OK) {
- return status;
- }
-
- readVector(reply, secureStop);
- return reply.readInt32();
- }
-
- virtual status_t releaseSecureStops(Vector<uint8_t> const &ssRelease) {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- writeVector(data, ssRelease);
- status_t status = remote()->transact(RELEASE_SECURE_STOPS, data, &reply);
- if (status != OK) {
- return status;
- }
-
- return reply.readInt32();
- }
-
- virtual status_t removeSecureStop(Vector<uint8_t> const &ssid) {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- writeVector(data, ssid);
- status_t status = remote()->transact(REMOVE_SECURE_STOP, data, &reply);
- if (status != OK) {
- return status;
- }
-
- return reply.readInt32();
- }
-
- virtual status_t removeAllSecureStops() {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- status_t status = remote()->transact(REMOVE_ALL_SECURE_STOPS, data, &reply);
- if (status != OK) {
- return status;
- }
-
- return reply.readInt32();
- }
-
- virtual status_t getOfflineLicenseKeySetIds(List<Vector<uint8_t> > &keySetIds) const {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- status_t status = remote()->transact(GET_OFFLINE_LICENSE_KEYSET_IDS, data, &reply);
- if (status != OK) {
- return status;
- }
-
- keySetIds.clear();
- uint32_t count = reply.readInt32();
- for (size_t i = 0; i < count; i++) {
- Vector<uint8_t> keySetId;
- readVector(reply, keySetId);
- keySetIds.push_back(keySetId);
- }
- return reply.readInt32();
- }
-
- virtual status_t removeOfflineLicense(Vector<uint8_t> const &keySetId) {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- writeVector(data, keySetId);
- status_t status = remote()->transact(REMOVE_OFFLINE_LICENSE, data, &reply);
- if (status != OK) {
- return status;
- }
- return reply.readInt32();
- }
-
- virtual status_t getOfflineLicenseState(Vector<uint8_t> const &keySetId,
- DrmPlugin::OfflineLicenseState *licenseState) const {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- writeVector(data, keySetId);
- status_t status = remote()->transact(GET_OFFLINE_LICENSE_STATE, data, &reply);
- if (status != OK) {
- *licenseState = DrmPlugin::OfflineLicenseState::kOfflineLicenseStateUnknown;
- return status;
- }
- *licenseState = static_cast<DrmPlugin::OfflineLicenseState>(reply.readInt32());
- return reply.readInt32();
- }
-
- virtual status_t getPropertyString(String8 const &name, String8 &value) const {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- data.writeString8(name);
- status_t status = remote()->transact(GET_PROPERTY_STRING, data, &reply);
- if (status != OK) {
- return status;
- }
-
- value = reply.readString8();
- return reply.readInt32();
- }
-
- virtual status_t getHdcpLevels(DrmPlugin::HdcpLevel *connected,
- DrmPlugin::HdcpLevel *max) const {
- Parcel data, reply;
-
- if (connected == NULL || max == NULL) {
- return BAD_VALUE;
- }
-
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- status_t status = remote()->transact(GET_HDCP_LEVELS, data, &reply);
- if (status != OK) {
- return status;
- }
-
- *connected = static_cast<DrmPlugin::HdcpLevel>(reply.readInt32());
- *max = static_cast<DrmPlugin::HdcpLevel>(reply.readInt32());
- return reply.readInt32();
- }
-
- virtual status_t getNumberOfSessions(uint32_t *open, uint32_t *max) const {
- Parcel data, reply;
-
- if (open == NULL || max == NULL) {
- return BAD_VALUE;
- }
-
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- status_t status = remote()->transact(GET_NUMBER_OF_SESSIONS, data, &reply);
- if (status != OK) {
- return status;
- }
-
- *open = reply.readInt32();
- *max = reply.readInt32();
- return reply.readInt32();
- }
-
- virtual status_t getSecurityLevel(Vector<uint8_t> const &sessionId,
- DrmPlugin::SecurityLevel *level) const {
- Parcel data, reply;
-
- if (level == NULL) {
- return BAD_VALUE;
- }
-
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- writeVector(data, sessionId);
- status_t status = remote()->transact(GET_SECURITY_LEVEL, data, &reply);
- if (status != OK) {
- return status;
- }
-
- *level = static_cast<DrmPlugin::SecurityLevel>(reply.readInt32());
- return reply.readInt32();
- }
-
- virtual status_t getPropertyByteArray(String8 const &name, Vector<uint8_t> &value) const {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- data.writeString8(name);
- status_t status = remote()->transact(GET_PROPERTY_BYTE_ARRAY, data, &reply);
- if (status != OK) {
- return status;
- }
-
- readVector(reply, value);
- return reply.readInt32();
- }
-
- virtual status_t setPropertyString(String8 const &name, String8 const &value) const {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- data.writeString8(name);
- data.writeString8(value);
- status_t status = remote()->transact(SET_PROPERTY_STRING, data, &reply);
- if (status != OK) {
- return status;
- }
-
- return reply.readInt32();
- }
-
- virtual status_t setPropertyByteArray(String8 const &name,
- Vector<uint8_t> const &value) const {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- data.writeString8(name);
- writeVector(data, value);
- status_t status = remote()->transact(SET_PROPERTY_BYTE_ARRAY, data, &reply);
- if (status != OK) {
- return status;
- }
-
- return reply.readInt32();
- }
-
- virtual status_t getMetrics(os::PersistableBundle *metrics) {
- if (metrics == NULL) {
- return BAD_VALUE;
- }
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- status_t status = remote()->transact(GET_METRICS, data, &reply);
- if (status != OK) {
- return status;
- }
- // The reply data is ordered as
- // 1) 32 bit integer reply followed by
- // 2) Serialized PersistableBundle containing metrics.
- status_t reply_status;
- if (reply.readInt32(&reply_status) != OK
- || reply_status != OK) {
- ALOGE("Failed to read getMetrics response code from parcel. %d",
- reply_status);
- return reply_status;
- }
-
- status = metrics->readFromParcel(&reply);
- if (status != OK) {
- ALOGE("Failed to read metrics from parcel. %d", status);
- return status;
- }
- return reply_status;
- }
-
- virtual status_t setCipherAlgorithm(Vector<uint8_t> const &sessionId,
- String8 const &algorithm) {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- writeVector(data, sessionId);
- data.writeString8(algorithm);
- status_t status = remote()->transact(SET_CIPHER_ALGORITHM, data, &reply);
- if (status != OK) {
- return status;
- }
- return reply.readInt32();
- }
-
- virtual status_t setMacAlgorithm(Vector<uint8_t> const &sessionId,
- String8 const &algorithm) {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- writeVector(data, sessionId);
- data.writeString8(algorithm);
- status_t status = remote()->transact(SET_MAC_ALGORITHM, data, &reply);
- if (status != OK) {
- return status;
- }
- return reply.readInt32();
- }
-
- virtual status_t encrypt(Vector<uint8_t> const &sessionId,
- Vector<uint8_t> const &keyId,
- Vector<uint8_t> const &input,
- Vector<uint8_t> const &iv,
- Vector<uint8_t> &output) {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- writeVector(data, sessionId);
- writeVector(data, keyId);
- writeVector(data, input);
- writeVector(data, iv);
-
- status_t status = remote()->transact(ENCRYPT, data, &reply);
- if (status != OK) {
- return status;
- }
- readVector(reply, output);
-
- return reply.readInt32();
- }
-
- virtual status_t decrypt(Vector<uint8_t> const &sessionId,
- Vector<uint8_t> const &keyId,
- Vector<uint8_t> const &input,
- Vector<uint8_t> const &iv,
- Vector<uint8_t> &output) {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- writeVector(data, sessionId);
- writeVector(data, keyId);
- writeVector(data, input);
- writeVector(data, iv);
-
- status_t status = remote()->transact(DECRYPT, data, &reply);
- if (status != OK) {
- return status;
- }
- readVector(reply, output);
-
- return reply.readInt32();
- }
-
- virtual status_t sign(Vector<uint8_t> const &sessionId,
- Vector<uint8_t> const &keyId,
- Vector<uint8_t> const &message,
- Vector<uint8_t> &signature) {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- writeVector(data, sessionId);
- writeVector(data, keyId);
- writeVector(data, message);
-
- status_t status = remote()->transact(SIGN, data, &reply);
- if (status != OK) {
- return status;
- }
- readVector(reply, signature);
-
- return reply.readInt32();
- }
-
- virtual status_t verify(Vector<uint8_t> const &sessionId,
- Vector<uint8_t> const &keyId,
- Vector<uint8_t> const &message,
- Vector<uint8_t> const &signature,
- bool &match) {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- writeVector(data, sessionId);
- writeVector(data, keyId);
- writeVector(data, message);
- writeVector(data, signature);
-
- status_t status = remote()->transact(VERIFY, data, &reply);
- if (status != OK) {
- return status;
- }
- match = (bool)reply.readInt32();
- return reply.readInt32();
- }
-
- virtual status_t signRSA(Vector<uint8_t> const &sessionId,
- String8 const &algorithm,
- Vector<uint8_t> const &message,
- Vector<uint8_t> const &wrappedKey,
- Vector<uint8_t> &signature) {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- writeVector(data, sessionId);
- data.writeString8(algorithm);
- writeVector(data, message);
- writeVector(data, wrappedKey);
-
- status_t status = remote()->transact(SIGN_RSA, data, &reply);
- if (status != OK) {
- return status;
- }
- readVector(reply, signature);
-
- return reply.readInt32();
- }
-
- virtual status_t setListener(const sp<IDrmClient>& listener) {
- Parcel data, reply;
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
- data.writeStrongBinder(IInterface::asBinder(listener));
- status_t status = remote()->transact(SET_LISTENER, data, &reply);
- if (status != OK) {
- return status;
- }
- return reply.readInt32();
- }
-
-private:
- void readVector(Parcel &reply, Vector<uint8_t> &vector) const {
- uint32_t size = reply.readInt32();
- vector.insertAt((size_t)0, size);
- reply.read(vector.editArray(), size);
- }
-
- void writeVector(Parcel &data, Vector<uint8_t> const &vector) const {
- data.writeInt32(vector.size());
- data.write(vector.array(), vector.size());
- }
-
- DISALLOW_EVIL_CONSTRUCTORS(BpDrm);
-};
-
-IMPLEMENT_META_INTERFACE(Drm, "android.drm.IDrm");
-
-////////////////////////////////////////////////////////////////////////////////
-
-void BnDrm::readVector(const Parcel &data, Vector<uint8_t> &vector) const {
- uint32_t size = data.readInt32();
- if (vector.insertAt((size_t)0, size) < 0) {
- vector.clear();
- }
- if (data.read(vector.editArray(), size) != NO_ERROR) {
- vector.clear();
- android_errorWriteWithInfoLog(0x534e4554, "62872384", -1, NULL, 0);
- }
-}
-
-void BnDrm::writeVector(Parcel *reply, Vector<uint8_t> const &vector) const {
- reply->writeInt32(vector.size());
- reply->write(vector.array(), vector.size());
-}
-
-status_t BnDrm::onTransact(
- uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
- switch (code) {
- case INIT_CHECK:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- reply->writeInt32(initCheck());
- return OK;
- }
-
- case IS_CRYPTO_SUPPORTED:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- uint8_t uuid[16];
- data.read(uuid, sizeof(uuid));
- String8 mimeType = data.readString8();
- DrmPlugin::SecurityLevel level =
- static_cast<DrmPlugin::SecurityLevel>(data.readInt32());
- bool isSupported = false;
- status_t result = isCryptoSchemeSupported(uuid, mimeType, level, &isSupported);
- reply->writeInt32(isSupported);
- reply->writeInt32(result);
- return OK;
- }
-
- case CREATE_PLUGIN:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- uint8_t uuid[16];
- data.read(uuid, sizeof(uuid));
- String8 appPackageName = data.readString8();
- reply->writeInt32(createPlugin(uuid, appPackageName));
- return OK;
- }
-
- case DESTROY_PLUGIN:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- reply->writeInt32(destroyPlugin());
- return OK;
- }
-
- case OPEN_SESSION:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- DrmPlugin::SecurityLevel level =
- static_cast<DrmPlugin::SecurityLevel>(data.readInt32());
- Vector<uint8_t> sessionId;
- status_t result = openSession(level, sessionId);
- writeVector(reply, sessionId);
- reply->writeInt32(result);
- return OK;
- }
-
- case CLOSE_SESSION:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- Vector<uint8_t> sessionId;
- readVector(data, sessionId);
- reply->writeInt32(closeSession(sessionId));
- return OK;
- }
-
- case GET_KEY_REQUEST:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- Vector<uint8_t> sessionId, initData;
-
- readVector(data, sessionId);
- readVector(data, initData);
- String8 mimeType = data.readString8();
- DrmPlugin::KeyType keyType = (DrmPlugin::KeyType)data.readInt32();
-
- KeyedVector<String8, String8> optionalParameters;
- uint32_t count = data.readInt32();
- for (size_t i = 0; i < count; ++i) {
- String8 key, value;
- key = data.readString8();
- value = data.readString8();
- optionalParameters.add(key, value);
- }
-
- Vector<uint8_t> request;
- String8 defaultUrl;
- DrmPlugin::KeyRequestType keyRequestType = DrmPlugin::kKeyRequestType_Unknown;
-
- status_t result = getKeyRequest(sessionId, initData, mimeType,
- keyType, optionalParameters, request, defaultUrl,
- &keyRequestType);
-
- writeVector(reply, request);
- reply->writeString8(defaultUrl);
- reply->writeInt32(static_cast<int32_t>(keyRequestType));
- reply->writeInt32(result);
- return OK;
- }
-
- case PROVIDE_KEY_RESPONSE:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- Vector<uint8_t> sessionId, response, keySetId;
- readVector(data, sessionId);
- readVector(data, response);
- uint32_t result = provideKeyResponse(sessionId, response, keySetId);
- writeVector(reply, keySetId);
- reply->writeInt32(result);
- return OK;
- }
-
- case REMOVE_KEYS:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- Vector<uint8_t> keySetId;
- readVector(data, keySetId);
- reply->writeInt32(removeKeys(keySetId));
- return OK;
- }
-
- case RESTORE_KEYS:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- Vector<uint8_t> sessionId, keySetId;
- readVector(data, sessionId);
- readVector(data, keySetId);
- reply->writeInt32(restoreKeys(sessionId, keySetId));
- return OK;
- }
-
- case QUERY_KEY_STATUS:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- Vector<uint8_t> sessionId;
- readVector(data, sessionId);
- KeyedVector<String8, String8> infoMap;
- status_t result = queryKeyStatus(sessionId, infoMap);
- size_t count = infoMap.size();
- reply->writeInt32(count);
- for (size_t i = 0; i < count; ++i) {
- reply->writeString8(infoMap.keyAt(i));
- reply->writeString8(infoMap.valueAt(i));
- }
- reply->writeInt32(result);
- return OK;
- }
-
- case GET_PROVISION_REQUEST:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- String8 certType = data.readString8();
- String8 certAuthority = data.readString8();
-
- Vector<uint8_t> request;
- String8 defaultUrl;
- status_t result = getProvisionRequest(certType, certAuthority,
- request, defaultUrl);
- writeVector(reply, request);
- reply->writeString8(defaultUrl);
- reply->writeInt32(result);
- return OK;
- }
-
- case PROVIDE_PROVISION_RESPONSE:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- Vector<uint8_t> response;
- Vector<uint8_t> certificate;
- Vector<uint8_t> wrappedKey;
- readVector(data, response);
- status_t result = provideProvisionResponse(response, certificate, wrappedKey);
- writeVector(reply, certificate);
- writeVector(reply, wrappedKey);
- reply->writeInt32(result);
- return OK;
- }
-
- case GET_SECURE_STOPS:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- List<Vector<uint8_t> > secureStops;
- status_t result = getSecureStops(secureStops);
- size_t count = secureStops.size();
- reply->writeInt32(count);
- List<Vector<uint8_t> >::iterator iter = secureStops.begin();
- while(iter != secureStops.end()) {
- size_t size = iter->size();
- reply->writeInt32(size);
- reply->write(iter->array(), iter->size());
- iter++;
- }
- reply->writeInt32(result);
- return OK;
- }
-
- case GET_SECURE_STOP_IDS:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- List<Vector<uint8_t> > secureStopIds;
- status_t result = getSecureStopIds(secureStopIds);
- size_t count = secureStopIds.size();
- reply->writeInt32(count);
- List<Vector<uint8_t> >::iterator iter = secureStopIds.begin();
- while(iter != secureStopIds.end()) {
- size_t size = iter->size();
- reply->writeInt32(size);
- reply->write(iter->array(), iter->size());
- iter++;
- }
- reply->writeInt32(result);
- return OK;
- }
-
- case GET_SECURE_STOP:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- Vector<uint8_t> ssid, secureStop;
- readVector(data, ssid);
- status_t result = getSecureStop(ssid, secureStop);
- writeVector(reply, secureStop);
- reply->writeInt32(result);
- return OK;
- }
-
- case RELEASE_SECURE_STOPS:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- Vector<uint8_t> ssRelease;
- readVector(data, ssRelease);
- reply->writeInt32(releaseSecureStops(ssRelease));
- return OK;
- }
-
- case REMOVE_SECURE_STOP:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- Vector<uint8_t> ssid;
- readVector(data, ssid);
- reply->writeInt32(removeSecureStop(ssid));
- return OK;
- }
-
- case REMOVE_ALL_SECURE_STOPS:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- reply->writeInt32(removeAllSecureStops());
- return OK;
- }
-
- case GET_HDCP_LEVELS:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- DrmPlugin::HdcpLevel connected = DrmPlugin::kHdcpLevelUnknown;
- DrmPlugin::HdcpLevel max = DrmPlugin::kHdcpLevelUnknown;
- status_t result = getHdcpLevels(&connected, &max);
- reply->writeInt32(connected);
- reply->writeInt32(max);
- reply->writeInt32(result);
- return OK;
- }
-
- case GET_NUMBER_OF_SESSIONS:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- uint32_t open = 0, max = 0;
- status_t result = getNumberOfSessions(&open, &max);
- reply->writeInt32(open);
- reply->writeInt32(max);
- reply->writeInt32(result);
- return OK;
- }
-
- case GET_SECURITY_LEVEL:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- Vector<uint8_t> sessionId;
- readVector(data, sessionId);
- DrmPlugin::SecurityLevel level = DrmPlugin::kSecurityLevelUnknown;
- status_t result = getSecurityLevel(sessionId, &level);
- reply->writeInt32(level);
- reply->writeInt32(result);
- return OK;
- }
-
- case GET_OFFLINE_LICENSE_KEYSET_IDS:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- List<Vector<uint8_t> > keySetIds;
- status_t result = getOfflineLicenseKeySetIds(keySetIds);
- size_t count = keySetIds.size();
- reply->writeInt32(count);
- List<Vector<uint8_t> >::iterator iter = keySetIds.begin();
- while(iter != keySetIds.end()) {
- size_t size = iter->size();
- reply->writeInt32(size);
- reply->write(iter->array(), iter->size());
- iter++;
- }
- reply->writeInt32(result);
- return OK;
- }
-
- case REMOVE_OFFLINE_LICENSE:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- Vector<uint8_t> keySetId;
- readVector(data, keySetId);
- reply->writeInt32(removeOfflineLicense(keySetId));
- return OK;
- }
-
- case GET_OFFLINE_LICENSE_STATE:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- Vector<uint8_t> keySetId;
- readVector(data, keySetId);
- DrmPlugin::OfflineLicenseState state;
- status_t result = getOfflineLicenseState(keySetId, &state);
- reply->writeInt32(static_cast<DrmPlugin::OfflineLicenseState>(state));
- reply->writeInt32(result);
- return OK;
- }
-
- case GET_PROPERTY_STRING:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- String8 name = data.readString8();
- String8 value;
- status_t result = getPropertyString(name, value);
- reply->writeString8(value);
- reply->writeInt32(result);
- return OK;
- }
-
- case GET_PROPERTY_BYTE_ARRAY:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- String8 name = data.readString8();
- Vector<uint8_t> value;
- status_t result = getPropertyByteArray(name, value);
- writeVector(reply, value);
- reply->writeInt32(result);
- return OK;
- }
-
- case SET_PROPERTY_STRING:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- String8 name = data.readString8();
- String8 value = data.readString8();
- reply->writeInt32(setPropertyString(name, value));
- return OK;
- }
-
- case SET_PROPERTY_BYTE_ARRAY:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- String8 name = data.readString8();
- Vector<uint8_t> value;
- readVector(data, value);
- reply->writeInt32(setPropertyByteArray(name, value));
- return OK;
- }
-
- case GET_METRICS:
- {
- CHECK_INTERFACE(IDrm, data, reply);
-
- os::PersistableBundle metrics;
- status_t result = getMetrics(&metrics);
- // The reply data is ordered as
- // 1) 32 bit integer reply followed by
- // 2) Serialized PersistableBundle containing metrics.
- // Only write the metrics if the getMetrics result was
- // OK and we successfully added the status to reply.
- status_t parcel_result = reply->writeInt32(result);
- if (result == OK && parcel_result == OK) {
- parcel_result = metrics.writeToParcel(reply);
- }
- return parcel_result;
- }
-
- case SET_CIPHER_ALGORITHM:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- Vector<uint8_t> sessionId;
- readVector(data, sessionId);
- String8 algorithm = data.readString8();
- reply->writeInt32(setCipherAlgorithm(sessionId, algorithm));
- return OK;
- }
-
- case SET_MAC_ALGORITHM:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- Vector<uint8_t> sessionId;
- readVector(data, sessionId);
- String8 algorithm = data.readString8();
- reply->writeInt32(setMacAlgorithm(sessionId, algorithm));
- return OK;
- }
-
- case ENCRYPT:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- Vector<uint8_t> sessionId, keyId, input, iv, output;
- readVector(data, sessionId);
- readVector(data, keyId);
- readVector(data, input);
- readVector(data, iv);
- uint32_t result = encrypt(sessionId, keyId, input, iv, output);
- writeVector(reply, output);
- reply->writeInt32(result);
- return OK;
- }
-
- case DECRYPT:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- Vector<uint8_t> sessionId, keyId, input, iv, output;
- readVector(data, sessionId);
- readVector(data, keyId);
- readVector(data, input);
- readVector(data, iv);
- uint32_t result = decrypt(sessionId, keyId, input, iv, output);
- writeVector(reply, output);
- reply->writeInt32(result);
- return OK;
- }
-
- case SIGN:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- Vector<uint8_t> sessionId, keyId, message, signature;
- readVector(data, sessionId);
- readVector(data, keyId);
- readVector(data, message);
- uint32_t result = sign(sessionId, keyId, message, signature);
- writeVector(reply, signature);
- reply->writeInt32(result);
- return OK;
- }
-
- case VERIFY:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- Vector<uint8_t> sessionId, keyId, message, signature;
- readVector(data, sessionId);
- readVector(data, keyId);
- readVector(data, message);
- readVector(data, signature);
- bool match = false;
- uint32_t result = verify(sessionId, keyId, message, signature, match);
- reply->writeInt32(match);
- reply->writeInt32(result);
- return OK;
- }
-
- case SIGN_RSA:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- Vector<uint8_t> sessionId, message, wrappedKey, signature;
- readVector(data, sessionId);
- String8 algorithm = data.readString8();
- readVector(data, message);
- readVector(data, wrappedKey);
- uint32_t result = signRSA(sessionId, algorithm, message, wrappedKey, signature);
- writeVector(reply, signature);
- reply->writeInt32(result);
- return OK;
- }
-
- case SET_LISTENER: {
- CHECK_INTERFACE(IDrm, data, reply);
- sp<IDrmClient> listener =
- interface_cast<IDrmClient>(data.readStrongBinder());
- reply->writeInt32(setListener(listener));
- return NO_ERROR;
- } break;
-
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-} // namespace android
diff --git a/drm/libmediadrm/IDrmClient.cpp b/drm/libmediadrm/IDrmClient.cpp
deleted file mode 100644
index 357de9d..0000000
--- a/drm/libmediadrm/IDrmClient.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
-**
-** Copyright 2013, 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 "IDrmClient"
-#include <utils/Log.h>
-
-#include <utils/RefBase.h>
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-
-#include <media/IMediaPlayerClient.h>
-#include <mediadrm/IDrmClient.h>
-
-namespace android {
-
-enum {
- NOTIFY = IBinder::FIRST_CALL_TRANSACTION,
-};
-
-class BpDrmClient: public BpInterface<IDrmClient>
-{
-public:
- explicit BpDrmClient(const sp<IBinder>& impl)
- : BpInterface<IDrmClient>(impl)
- {
- }
-
- virtual void notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IDrmClient::getInterfaceDescriptor());
- data.writeInt32((int)eventType);
- data.writeInt32(extra);
- if (obj && obj->dataSize() > 0) {
- data.appendFrom(const_cast<Parcel *>(obj), 0, obj->dataSize());
- }
- remote()->transact(NOTIFY, data, &reply, IBinder::FLAG_ONEWAY);
- }
-};
-
-IMPLEMENT_META_INTERFACE(DrmClient, "android.media.IDrmClient");
-
-// ----------------------------------------------------------------------
-
-status_t BnDrmClient::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch (code) {
- case NOTIFY: {
- CHECK_INTERFACE(IDrmClient, data, reply);
- int eventType = data.readInt32();
- int extra = data.readInt32();
- Parcel obj;
- if (data.dataAvail() > 0) {
- obj.appendFrom(const_cast<Parcel *>(&data), data.dataPosition(), data.dataAvail());
- }
-
- notify((DrmPlugin::EventType)eventType, extra, &obj);
- return NO_ERROR;
- } break;
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-} // namespace android
diff --git a/drm/libmediadrm/IMediaDrmService.cpp b/drm/libmediadrm/IMediaDrmService.cpp
deleted file mode 100644
index f320d0b..0000000
--- a/drm/libmediadrm/IMediaDrmService.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
-**
-** 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.
-*/
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/Parcel.h>
-#include <binder/IMemory.h>
-#include <mediadrm/ICrypto.h>
-#include <mediadrm/IDrm.h>
-#include <mediadrm/IMediaDrmService.h>
-
-#include <utils/Errors.h> // for status_t
-#include <utils/String8.h>
-
-namespace android {
-
-enum {
- MAKE_CRYPTO = IBinder::FIRST_CALL_TRANSACTION,
- MAKE_DRM,
-};
-
-class BpMediaDrmService: public BpInterface<IMediaDrmService>
-{
-public:
- explicit BpMediaDrmService(const sp<IBinder>& impl)
- : BpInterface<IMediaDrmService>(impl)
- {
- }
-
- virtual sp<ICrypto> makeCrypto() {
- Parcel data, reply;
- data.writeInterfaceToken(IMediaDrmService::getInterfaceDescriptor());
- remote()->transact(MAKE_CRYPTO, data, &reply);
- return interface_cast<ICrypto>(reply.readStrongBinder());
- }
-
- virtual sp<IDrm> makeDrm() {
- Parcel data, reply;
- data.writeInterfaceToken(IMediaDrmService::getInterfaceDescriptor());
- remote()->transact(MAKE_DRM, data, &reply);
- return interface_cast<IDrm>(reply.readStrongBinder());
- }
-
-};
-
-IMPLEMENT_META_INTERFACE(MediaDrmService, "android.media.IMediaDrmService");
-
-// ----------------------------------------------------------------------
-
-status_t BnMediaDrmService::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch (code) {
- case MAKE_CRYPTO: {
- CHECK_INTERFACE(IMediaDrmService, data, reply);
- sp<ICrypto> crypto = makeCrypto();
- reply->writeStrongBinder(IInterface::asBinder(crypto));
- return NO_ERROR;
- } break;
- case MAKE_DRM: {
- CHECK_INTERFACE(IMediaDrmService, data, reply);
- sp<IDrm> drm = makeDrm();
- reply->writeStrongBinder(IInterface::asBinder(drm));
- return NO_ERROR;
- } break;
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-// ----------------------------------------------------------------------------
-
-} // namespace android
diff --git a/drm/libmediadrm/PluginMetricsReporting.cpp b/drm/libmediadrm/PluginMetricsReporting.cpp
index 8cd6f96..b0abf83 100644
--- a/drm/libmediadrm/PluginMetricsReporting.cpp
+++ b/drm/libmediadrm/PluginMetricsReporting.cpp
@@ -21,7 +21,7 @@
#include <inttypes.h>
-#include <media/MediaAnalyticsItem.h>
+#include <media/MediaMetrics.h>
#include <utils/Log.h>
@@ -33,20 +33,18 @@
status_t reportVendorMetrics(const std::string& metrics,
const String8& name,
- const String8& appPackageName) {
- std::unique_ptr<MediaAnalyticsItem> analyticsItem(MediaAnalyticsItem::create(name.c_str()));
- analyticsItem->generateSessionID();
-
- std::string app_package_name(appPackageName.c_str(), appPackageName.size());
- analyticsItem->setPkgName(app_package_name);
+ uid_t appUid) {
+ mediametrics_handle_t analyticsItem(mediametrics_create(name.c_str()));
+ mediametrics_setUid(analyticsItem, appUid);
if (metrics.size() > 0) {
- analyticsItem->setCString(kSerializedMetricsField, metrics.c_str());
+ mediametrics_setCString(analyticsItem, kSerializedMetricsField, metrics.c_str());
}
- if (!analyticsItem->selfrecord()) {
- ALOGE("selfrecord() returned false. sessioId %" PRId64, analyticsItem->getSessionID());
+ if (!mediametrics_selfRecord(analyticsItem)) {
+ ALOGE("%s: selfrecord() returned false", __func__);
}
+ mediametrics_delete(analyticsItem);
return OK;
}
@@ -69,13 +67,13 @@
status_t reportDrmPluginMetrics(const std::string& b64EncodedMetrics,
const String8& vendor,
const String8& description,
- const String8& appPackageName) {
+ uid_t appUid) {
String8 name = String8::format("drm.vendor.%s.%s",
sanitize(vendor).c_str(),
sanitize(description).c_str());
- return reportVendorMetrics(b64EncodedMetrics, name, appPackageName);
+ return reportVendorMetrics(b64EncodedMetrics, name, appUid);
}
} // namespace android
diff --git a/drm/libmediadrm/TEST_MAPPING b/drm/libmediadrm/TEST_MAPPING
new file mode 100644
index 0000000..bc15879
--- /dev/null
+++ b/drm/libmediadrm/TEST_MAPPING
@@ -0,0 +1,26 @@
+{
+ "presubmit": [
+ {
+ "name": "GtsMediaTestCases",
+ "options" : [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "include-filter": "com.google.android.media.gts.WidevineGenericOpsTests"
+ },
+ {
+ "include-filter": "com.google.android.media.gts.MediaDrmTest"
+ },
+ {
+ "include-filter": "com.google.android.media.gts.WidevineDashPolicyTests"
+ }
+ ]
+ }
+ ],
+ "imports": [
+ {
+ "path": "frameworks/av/drm/mediadrm/plugins"
+ }
+ ]
+}
diff --git a/drm/libmediadrm/include/mediadrm/CryptoHal.h b/drm/libmediadrm/include/mediadrm/CryptoHal.h
index 73c029f..c9fda67 100644
--- a/drm/libmediadrm/include/mediadrm/CryptoHal.h
+++ b/drm/libmediadrm/include/mediadrm/CryptoHal.h
@@ -31,12 +31,15 @@
using drm::V1_0::ICryptoFactory;
using drm::V1_0::ICryptoPlugin;
using drm::V1_0::SharedBuffer;
+using drm::V1_0::DestinationBuffer;
+
+using ::android::hardware::HidlMemory;
class IMemoryHeap;
namespace android {
-struct CryptoHal : public BnCrypto {
+struct CryptoHal : public ICrypto {
CryptoHal();
virtual ~CryptoHal();
@@ -58,12 +61,12 @@
virtual ssize_t decrypt(const uint8_t key[16], const uint8_t iv[16],
CryptoPlugin::Mode mode, const CryptoPlugin::Pattern &pattern,
- const ICrypto::SourceBuffer &source, size_t offset,
+ const ::SharedBuffer &source, size_t offset,
const CryptoPlugin::SubSample *subSamples, size_t numSubSamples,
- const ICrypto::DestinationBuffer &destination,
+ const ::DestinationBuffer &destination,
AString *errorDetailMsg);
- virtual int32_t setHeap(const sp<IMemoryHeap>& heap) {
+ virtual int32_t setHeap(const sp<HidlMemory>& heap) {
return setHeapBase(heap);
}
virtual void unsetHeap(int32_t seqNum) { clearHeapBase(seqNum); }
@@ -83,31 +86,17 @@
*/
status_t mInitCheck;
- struct HeapBase {
- HeapBase() : mBufferId(0), mSize(0) {}
- HeapBase(uint32_t bufferId, size_t size) :
- mBufferId(bufferId), mSize(size) {}
-
- uint32_t getBufferId() const {return mBufferId;}
- size_t getSize() const {return mSize;}
-
- private:
- uint32_t mBufferId;
- size_t mSize;
- };
-
- KeyedVector<int32_t, HeapBase> mHeapBases;
- uint32_t mNextBufferId;
+ KeyedVector<int32_t, size_t> mHeapSizes;
int32_t mHeapSeqNum;
Vector<sp<ICryptoFactory>> makeCryptoFactories();
sp<ICryptoPlugin> makeCryptoPlugin(const sp<ICryptoFactory>& factory,
const uint8_t uuid[16], const void *initData, size_t size);
- int32_t setHeapBase(const sp<IMemoryHeap>& heap);
+ int32_t setHeapBase(const sp<HidlMemory>& heap);
void clearHeapBase(int32_t seqNum);
- status_t toSharedBuffer(const sp<IMemory>& memory, int32_t seqNum, ::SharedBuffer* buffer);
+ status_t checkSharedBuffer(const ::SharedBuffer& buffer);
DISALLOW_EVIL_CONSTRUCTORS(CryptoHal);
};
diff --git a/drm/libmediadrm/include/mediadrm/DrmHal.h b/drm/libmediadrm/include/mediadrm/DrmHal.h
index 542d300..3b4639b 100644
--- a/drm/libmediadrm/include/mediadrm/DrmHal.h
+++ b/drm/libmediadrm/include/mediadrm/DrmHal.h
@@ -26,12 +26,11 @@
#include <android/hardware/drm/1.2/IDrmPlugin.h>
#include <android/hardware/drm/1.2/IDrmPluginListener.h>
-#include <media/IResourceManagerService.h>
-#include <media/MediaAnalyticsItem.h>
#include <mediadrm/DrmMetrics.h>
#include <mediadrm/DrmSessionManager.h>
#include <mediadrm/IDrm.h>
#include <mediadrm/IDrmClient.h>
+#include <mediadrm/IDrmMetricsConsumer.h>
#include <utils/threads.h>
namespace drm = ::android::hardware::drm;
@@ -58,28 +57,10 @@
return memcmp(l.array(), r.array(), l.size()) == 0;
}
-struct DrmHal : public BnDrm,
- public IBinder::DeathRecipient,
+struct DrmHal : public IDrm,
public IDrmPluginListener_V1_2 {
- struct DrmSessionClient : public BnResourceManagerClient {
- explicit DrmSessionClient(DrmHal* drm, const Vector<uint8_t>& sessionId)
- : mSessionId(sessionId),
- mDrm(drm) {}
-
- virtual bool reclaimResource();
- virtual String8 getName();
-
- const Vector<uint8_t> mSessionId;
-
- protected:
- virtual ~DrmSessionClient();
-
- private:
- wp<DrmHal> mDrm;
-
- DISALLOW_EVIL_CONSTRUCTORS(DrmSessionClient);
- };
+ struct DrmSessionClient;
DrmHal();
virtual ~DrmHal();
@@ -156,7 +137,7 @@
virtual status_t setPropertyString(String8 const &name, String8 const &value ) const;
virtual status_t setPropertyByteArray(String8 const &name,
Vector<uint8_t> const &value ) const;
- virtual status_t getMetrics(os::PersistableBundle *metrics);
+ virtual status_t getMetrics(const sp<IDrmMetricsConsumer> &consumer);
virtual status_t setCipherAlgorithm(Vector<uint8_t> const &sessionId,
String8 const &algorithm);
@@ -210,8 +191,6 @@
Return<void> sendSessionLostState(const hidl_vec<uint8_t>& sessionId);
- virtual void binderDied(const wp<IBinder> &the_late_who);
-
private:
static Mutex mLock;
@@ -219,7 +198,7 @@
mutable Mutex mEventLock;
mutable Mutex mNotifyLock;
- const Vector<sp<IDrmFactory>> mFactories;
+ const std::vector<sp<IDrmFactory>> mFactories;
sp<IDrmPlugin> mPlugin;
sp<drm::V1_1::IDrmPlugin> mPluginV1_1;
sp<drm::V1_2::IDrmPlugin> mPluginV1_2;
@@ -228,7 +207,7 @@
// Mutable to allow modification within GetPropertyByteArray.
mutable MediaDrmMetrics mMetrics;
- Vector<sp<DrmSessionClient>> mOpenSessions;
+ std::vector<std::shared_ptr<DrmSessionClient>> mOpenSessions;
void closeOpenSessions();
void cleanup();
@@ -240,7 +219,7 @@
*/
status_t mInitCheck;
- Vector<sp<IDrmFactory>> makeDrmFactories();
+ std::vector<sp<IDrmFactory>> makeDrmFactories();
sp<IDrmPlugin> makeDrmPlugin(const sp<IDrmFactory>& factory,
const uint8_t uuid[16], const String8& appPackageName);
diff --git a/drm/libmediadrm/include/mediadrm/DrmMetrics.h b/drm/libmediadrm/include/mediadrm/DrmMetrics.h
index 6f132bf..100b8f7 100644
--- a/drm/libmediadrm/include/mediadrm/DrmMetrics.h
+++ b/drm/libmediadrm/include/mediadrm/DrmMetrics.h
@@ -25,6 +25,7 @@
#include <binder/PersistableBundle.h>
#include <media/CounterMetric.h>
#include <media/EventMetric.h>
+#include <sys/types.h>
namespace android {
@@ -71,8 +72,8 @@
void SetAppPackageName(const String8& appPackageName) { mAppPackageName = appPackageName; }
const String8& GetAppPackageName() { return mAppPackageName; }
- // Export the metrics to a PersistableBundle.
- void Export(os::PersistableBundle* metricsBundle);
+ void SetAppUid(uid_t appUid) { mAppUid = appUid; }
+ uid_t GetAppUid() const { return mAppUid; }
// Get the serialized metrics. Metrics are formatted as a serialized
// DrmFrameworkMetrics proto. If there is a failure serializing the metrics,
@@ -80,46 +81,8 @@
// caller and must not be null.
status_t GetSerializedMetrics(std::string* serializedMetrics);
- // Converts the DRM plugin metrics to a PersistableBundle. All of the metrics
- // found in |pluginMetrics| are added to the |metricsBundle| parameter.
- // |pluginBundle| is owned by the caller and must not be null.
- //
- // Each item in the pluginMetrics vector is added as a new PersistableBundle. E.g.
- // DrmMetricGroup {
- // metrics[0] {
- // name: "buf_copy"
- // attributes[0] {
- // name: "size"
- // type: INT64_TYPE
- // int64Value: 1024
- // }
- // values[0] {
- // componentName: "operation_count"
- // type: INT64_TYPE
- // int64Value: 75
- // }
- // values[1] {
- // component_name: "average_time_seconds"
- // type: DOUBLE_TYPE
- // doubleValue: 0.00000042
- // }
- // }
- // }
- //
- // becomes
- //
- // metricsBundle {
- // "0": (PersistableBundle) {
- // "attributes" : (PersistableBundle) {
- // "size" : (int64) 1024
- // }
- // "operation_count" : (int64) 75
- // "average_time_seconds" : (double) 0.00000042
- // }
- //
- static status_t HidlMetricsToBundle(
- const hardware::hidl_vec<hardware::drm::V1_1::DrmMetricGroup>& pluginMetrics,
- os::PersistableBundle* metricsBundle);
+ // Get copy of session lifetimes.
+ std::map<std::string, std::pair<int64_t, int64_t>> GetSessionLifespans() const;
protected:
// This is visible for testing only.
@@ -131,6 +94,7 @@
std::map<std::string, std::pair<int64_t, int64_t>> mSessionLifespans;
String8 mAppPackageName;
+ uid_t mAppUid{~0u};
};
} // namespace android
diff --git a/drm/libmediadrm/include/mediadrm/DrmMetricsConsumer.h b/drm/libmediadrm/include/mediadrm/DrmMetricsConsumer.h
new file mode 100644
index 0000000..bbbf4b5
--- /dev/null
+++ b/drm/libmediadrm/include/mediadrm/DrmMetricsConsumer.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.
+ */
+
+#include <binder/PersistableBundle.h>
+#include <mediadrm/IDrmMetricsConsumer.h>
+#include <utils/Errors.h>
+
+#ifndef ANDROID_METRICSCONSUMER_H_
+
+#define ANDROID_METRICSCONSUMER_H_
+
+namespace android {
+
+/**
+ * IDrmMetricsConsumer which saves IDrm/ICrypto metrics into a PersistableBundle.
+ *
+ * Example usage:
+ *
+ * PersistableBundle bundle;
+ * DrmMetricsConsumer consumer(&bundle);
+ * drm->exportMetrics(&consumer);
+ * crypto->exportMetrics(&consumer);
+ * // bundle now contains metrics from drm/crypto.
+ *
+ */
+struct DrmMetricsConsumer : public IDrmMetricsConsumer {
+ DrmMetricsConsumer(os::PersistableBundle *bundle) : mBundle(bundle) {}
+
+ status_t consumeFrameworkMetrics(const MediaDrmMetrics &) override;
+
+ status_t consumeHidlMetrics(
+ const String8 &/*vendor*/,
+ const hidl_vec<DrmMetricGroup> &/*pluginMetrics*/) override;
+
+ // Converts the DRM plugin metrics to a PersistableBundle. All of the metrics
+ // found in |pluginMetrics| are added to the |metricsBundle| parameter.
+ // |pluginBundle| is owned by the caller and must not be null.
+ //
+ // Each item in the pluginMetrics vector is added as a new PersistableBundle. E.g.
+ // DrmMetricGroup {
+ // metrics[0] {
+ // name: "buf_copy"
+ // attributes[0] {
+ // name: "size"
+ // type: INT64_TYPE
+ // int64Value: 1024
+ // }
+ // values[0] {
+ // componentName: "operation_count"
+ // type: INT64_TYPE
+ // int64Value: 75
+ // }
+ // values[1] {
+ // component_name: "average_time_seconds"
+ // type: DOUBLE_TYPE
+ // doubleValue: 0.00000042
+ // }
+ // }
+ // }
+ //
+ // becomes
+ //
+ // metricsBundle {
+ // "0": (PersistableBundle) {
+ // "attributes" : (PersistableBundle) {
+ // "size" : (int64) 1024
+ // }
+ // "operation_count" : (int64) 75
+ // "average_time_seconds" : (double) 0.00000042
+ // }
+ //
+ static status_t HidlMetricsToBundle(
+ const hardware::hidl_vec<hardware::drm::V1_1::DrmMetricGroup>& pluginMetrics,
+ os::PersistableBundle* metricsBundle);
+
+private:
+ os::PersistableBundle *mBundle;
+ DISALLOW_EVIL_CONSTRUCTORS(DrmMetricsConsumer);
+};
+
+} // namespace android
+
+#endif // ANDROID_METRICSCONSUMER_H_
diff --git a/drm/libmediadrm/include/mediadrm/DrmSessionManager.h b/drm/libmediadrm/include/mediadrm/DrmSessionManager.h
index b1ad580..9e43504 100644
--- a/drm/libmediadrm/include/mediadrm/DrmSessionManager.h
+++ b/drm/libmediadrm/include/mediadrm/DrmSessionManager.h
@@ -18,8 +18,9 @@
#define DRM_SESSION_MANAGER_H_
-#include <binder/IBinder.h>
-#include <media/IResourceManagerService.h>
+#include <aidl/android/media/IResourceManagerClient.h>
+#include <aidl/android/media/IResourceManagerService.h>
+#include <android/binder_auto_utils.h>
#include <media/stagefright/foundation/ABase.h>
#include <utils/RefBase.h>
#include <utils/KeyedVector.h>
@@ -27,13 +28,16 @@
#include <utils/Vector.h>
#include <map>
+#include <memory>
#include <utility>
#include <vector>
namespace android {
class DrmSessionManagerTest;
-class IResourceManagerClient;
+
+using aidl::android::media::IResourceManagerClient;
+using aidl::android::media::IResourceManagerService;
bool isEqualSessionId(const Vector<uint8_t> &sessionId1, const Vector<uint8_t> &sessionId2);
@@ -45,13 +49,15 @@
typedef std::map<std::vector<uint8_t>, SessionInfo> SessionInfoMap;
-struct DrmSessionManager : public IBinder::DeathRecipient {
+struct DrmSessionManager : public RefBase {
static sp<DrmSessionManager> Instance();
DrmSessionManager();
- explicit DrmSessionManager(const sp<IResourceManagerService> &service);
+ explicit DrmSessionManager(const std::shared_ptr<IResourceManagerService> &service);
- void addSession(int pid, const sp<IResourceManagerClient>& drm, const Vector<uint8_t>& sessionId);
+ void addSession(int pid,
+ const std::shared_ptr<IResourceManagerClient>& drm,
+ const Vector<uint8_t>& sessionId);
void useSession(const Vector<uint8_t>& sessionId);
void removeSession(const Vector<uint8_t>& sessionId);
bool reclaimSession(int callingPid);
@@ -61,7 +67,7 @@
bool containsSession(const Vector<uint8_t>& sessionId) const;
// implements DeathRecipient
- virtual void binderDied(const wp<IBinder>& /*who*/);
+ void binderDied();
protected:
virtual ~DrmSessionManager();
@@ -69,10 +75,11 @@
private:
void init();
- sp<IResourceManagerService> mService;
+ std::shared_ptr<IResourceManagerService> mService;
mutable Mutex mLock;
SessionInfoMap mSessionMap;
bool mInitialized;
+ ::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
DISALLOW_EVIL_CONSTRUCTORS(DrmSessionManager);
};
diff --git a/drm/libmediadrm/include/mediadrm/IDrm.h b/drm/libmediadrm/include/mediadrm/IDrm.h
index fbe80c6..0177c24 100644
--- a/drm/libmediadrm/include/mediadrm/IDrm.h
+++ b/drm/libmediadrm/include/mediadrm/IDrm.h
@@ -14,12 +14,10 @@
* limitations under the License.
*/
-#include <binder/IInterface.h>
-#include <binder/PersistableBundle.h>
#include <media/stagefright/foundation/ABase.h>
#include <media/drm/DrmAPI.h>
-#include <media/MediaAnalyticsItem.h>
#include <mediadrm/IDrmClient.h>
+#include <mediadrm/IDrmMetricsConsumer.h>
#ifndef ANDROID_IDRM_H_
@@ -29,8 +27,9 @@
struct AString;
-struct IDrm : public IInterface {
- DECLARE_META_INTERFACE(Drm);
+struct IDrm : public virtual RefBase {
+
+ virtual ~IDrm() {}
virtual status_t initCheck() const = 0;
@@ -107,7 +106,7 @@
virtual status_t setPropertyByteArray(String8 const &name,
Vector<uint8_t> const &value) const = 0;
- virtual status_t getMetrics(os::PersistableBundle *metrics) = 0;
+ virtual status_t getMetrics(const sp<IDrmMetricsConsumer> &consumer) = 0;
virtual status_t setCipherAlgorithm(Vector<uint8_t> const &sessionId,
String8 const &algorithm) = 0;
@@ -146,19 +145,13 @@
virtual status_t setListener(const sp<IDrmClient>& listener) = 0;
+protected:
+ IDrm() {}
+
private:
DISALLOW_EVIL_CONSTRUCTORS(IDrm);
};
-struct BnDrm : public BnInterface<IDrm> {
- virtual status_t onTransact(
- uint32_t code, const Parcel &data, Parcel *reply,
- uint32_t flags = 0);
-private:
- void readVector(const Parcel &data, Vector<uint8_t> &vector) const;
- void writeVector(Parcel *reply, Vector<uint8_t> const &vector) const;
-};
-
} // namespace android
#endif // ANDROID_IDRM_H_
diff --git a/drm/libmediadrm/include/mediadrm/IDrmClient.h b/drm/libmediadrm/include/mediadrm/IDrmClient.h
index 3b2fc7c..fe25ae1 100644
--- a/drm/libmediadrm/include/mediadrm/IDrmClient.h
+++ b/drm/libmediadrm/include/mediadrm/IDrmClient.h
@@ -18,29 +18,46 @@
#define ANDROID_IDRMCLIENT_H
#include <utils/RefBase.h>
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
+#include <hidl/HidlSupport.h>
#include <media/drm/DrmAPI.h>
+#include <cstdint>
+#include <vector>
+
namespace android {
-class IDrmClient: public IInterface
-{
-public:
- DECLARE_META_INTERFACE(DrmClient);
-
- virtual void notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj) = 0;
+struct DrmKeyStatus {
+ const uint32_t type;
+ const hardware::hidl_vec<uint8_t> keyId;
};
-// ----------------------------------------------------------------------------
-
-class BnDrmClient: public BnInterface<IDrmClient>
+class IDrmClient: public virtual RefBase
{
public:
- virtual status_t onTransact(uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
+ ~IDrmClient() {}
+
+ virtual void sendEvent(
+ DrmPlugin::EventType eventType,
+ const hardware::hidl_vec<uint8_t> &sessionId,
+ const hardware::hidl_vec<uint8_t> &data) = 0;
+
+ virtual void sendExpirationUpdate(
+ const hardware::hidl_vec<uint8_t> &sessionId,
+ int64_t expiryTimeInMS) = 0;
+
+ virtual void sendKeysChange(
+ const hardware::hidl_vec<uint8_t> &sessionId,
+ const std::vector<DrmKeyStatus> &keyStatusList,
+ bool hasNewUsableKey) = 0;
+
+ virtual void sendSessionLostState(
+ const hardware::hidl_vec<uint8_t> &sessionId) = 0;
+
+protected:
+ IDrmClient() {}
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(IDrmClient);
};
}; // namespace android
diff --git a/drm/libmediadrm/include/mediadrm/IDrmMetricsConsumer.h b/drm/libmediadrm/include/mediadrm/IDrmMetricsConsumer.h
new file mode 100644
index 0000000..aef35c3
--- /dev/null
+++ b/drm/libmediadrm/include/mediadrm/IDrmMetricsConsumer.h
@@ -0,0 +1,87 @@
+/*
+ * 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 <android/hardware/drm/1.1/types.h>
+#include <hidl/HidlSupport.h>
+#include <media/stagefright/foundation/ABase.h>
+
+#ifndef ANDROID_IDRMMETRICSCONSUMER_H_
+
+#define ANDROID_IDRMMETRICSCONSUMER_H_
+
+using ::android::hardware::hidl_vec;
+using ::android::hardware::drm::V1_1::DrmMetricGroup;
+
+namespace android {
+
+class MediaDrmMetrics;
+class String8;
+
+/**
+ * Interface to consume metrics produced by the IDrm/ICrypto
+ *
+ * To use with IDrm:
+ * drm->exportMetrics(&consumer);
+ *
+ * IDrmMetricsConsumer::consumeFrameworkMetrics &
+ * IDrmMetricsConsumer::consumeHidlMetrics implementations
+ * would each be invoked once per call to IDrm::exportMetrics.
+ * |consumeFrameworkMetrics| would be called for plugin-agnostic
+ * framework metrics; |consumeHidlMetrics| would be called for
+ * plugin specific metrics.
+ *
+ * ----------------------------------------
+ *
+ * To use with ICrypto:
+ * crypto->exportMetrics(&consumer);
+ *
+ * IDrmMetricsConsumer::consumeHidlMetrics implementation
+ * would each be invoked once per call to ICrypto::exportMetrics.
+ * ICrypto metrics are plugin agnostic.
+ *
+ * ----------------------------------------
+ *
+ * For an example implementation of IDrmMetricsConsumer, please
+ * see DrmMetricsConsumer. DrmMetricsConsumer consumes IDrm/ICrypto
+ * metrics and saves the metrics to a PersistableBundle.
+ *
+ */
+struct IDrmMetricsConsumer : public RefBase {
+
+ virtual ~IDrmMetricsConsumer() {}
+
+ /**
+ * Consume framework (plugin agnostic) MediaDrmMetrics
+ */
+ virtual status_t consumeFrameworkMetrics(const MediaDrmMetrics &) = 0;
+
+ /**
+ * Consume list of DrmMetricGroup with optional Drm vendor name
+ */
+ virtual status_t consumeHidlMetrics(
+ const String8 &vendor,
+ const hidl_vec<DrmMetricGroup> &pluginMetrics) = 0;
+
+protected:
+ IDrmMetricsConsumer() {}
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(IDrmMetricsConsumer);
+};
+
+} // namespace android
+
+#endif // ANDROID_IDRMMETRICSCONSUMER_H_
diff --git a/drm/libmediadrm/include/mediadrm/IMediaDrmService.h b/drm/libmediadrm/include/mediadrm/IMediaDrmService.h
deleted file mode 100644
index 323fae5..0000000
--- a/drm/libmediadrm/include/mediadrm/IMediaDrmService.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 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 ANDROID_IMEDIADRMSERVICE_H
-#define ANDROID_IMEDIADRMSERVICE_H
-
-#include <utils/Errors.h> // for status_t
-#include <utils/RefBase.h>
-#include <utils/String8.h>
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-
-namespace android {
-
-struct ICrypto;
-struct IDrm;
-
-class IMediaDrmService: public IInterface
-{
-public:
- DECLARE_META_INTERFACE(MediaDrmService);
-
- virtual sp<ICrypto> makeCrypto() = 0;
- virtual sp<IDrm> makeDrm() = 0;
-};
-
-// ----------------------------------------------------------------------------
-
-class BnMediaDrmService: public BnInterface<IMediaDrmService>
-{
-public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-}; // namespace android
-
-#endif // ANDROID_IMEDIADRMSERVICE_H
diff --git a/drm/libmediadrm/interface/mediadrm/DrmUtils.h b/drm/libmediadrm/interface/mediadrm/DrmUtils.h
new file mode 100644
index 0000000..20b3fe9
--- /dev/null
+++ b/drm/libmediadrm/interface/mediadrm/DrmUtils.h
@@ -0,0 +1,98 @@
+/*
+ * 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_DRMUTILS_H
+#define ANDROID_DRMUTILS_H
+
+#include <android/hardware/drm/1.0/ICryptoFactory.h>
+#include <android/hardware/drm/1.0/IDrmFactory.h>
+#include <utils/Errors.h> // for status_t
+#include <utils/StrongPointer.h>
+#include <vector>
+
+using namespace ::android::hardware::drm;
+
+namespace android {
+
+struct ICrypto;
+struct IDrm;
+
+namespace DrmUtils {
+
+bool UseDrmService();
+
+sp<IDrm> MakeDrm(status_t *pstatus = nullptr);
+
+sp<ICrypto> MakeCrypto(status_t *pstatus = nullptr);
+
+template<typename BA, typename PARCEL>
+void WriteByteArray(PARCEL &obj, const BA &vec) {
+ obj.writeInt32(vec.size());
+ if (vec.size()) {
+ obj.write(vec.data(), vec.size());
+ }
+}
+
+template<typename ET, typename BA, typename PARCEL>
+void WriteEventToParcel(
+ PARCEL &obj,
+ ET eventType,
+ const BA &sessionId,
+ const BA &data) {
+ WriteByteArray(obj, sessionId);
+ WriteByteArray(obj, data);
+ obj.writeInt32(eventType);
+}
+
+template<typename BA, typename PARCEL>
+void WriteExpirationUpdateToParcel(
+ PARCEL &obj,
+ const BA &sessionId,
+ int64_t expiryTimeInMS) {
+ WriteByteArray(obj, sessionId);
+ obj.writeInt64(expiryTimeInMS);
+}
+
+template<typename BA, typename KSL, typename PARCEL>
+void WriteKeysChange(
+ PARCEL &obj,
+ const BA &sessionId,
+ const KSL &keyStatusList,
+ bool hasNewUsableKey) {
+ WriteByteArray(obj, sessionId);
+ obj.writeInt32(keyStatusList.size());
+ for (const auto &keyStatus : keyStatusList) {
+ WriteByteArray(obj, keyStatus.keyId);
+ obj.writeInt32(keyStatus.type);
+ }
+ obj.writeInt32(hasNewUsableKey);
+}
+
+std::vector<sp<::V1_0::IDrmFactory>> MakeDrmFactories(const uint8_t uuid[16] = nullptr);
+
+std::vector<sp<::V1_0::IDrmPlugin>> MakeDrmPlugins(const uint8_t uuid[16],
+ const char *appPackageName);
+
+std::vector<sp<::V1_0::ICryptoFactory>> MakeCryptoFactories(const uint8_t uuid[16]);
+
+std::vector<sp<::V1_0::ICryptoPlugin>> MakeCryptoPlugins(const uint8_t uuid[16],
+ const void *initData, size_t initDataSize);
+
+} // namespace DrmUtils
+
+} // namespace android
+
+#endif // ANDROID_DRMUTILS_H
diff --git a/drm/libmediadrm/interface/mediadrm/ICrypto.h b/drm/libmediadrm/interface/mediadrm/ICrypto.h
index 6d896b8..df980ae 100644
--- a/drm/libmediadrm/interface/mediadrm/ICrypto.h
+++ b/drm/libmediadrm/interface/mediadrm/ICrypto.h
@@ -14,23 +14,38 @@
* limitations under the License.
*/
-#include <binder/IInterface.h>
#include <cutils/native_handle.h>
#include <media/hardware/CryptoAPI.h>
#include <media/stagefright/foundation/ABase.h>
+#include <utils/RefBase.h>
+#include <utils/StrongPointer.h>
#ifndef ANDROID_ICRYPTO_H_
#define ANDROID_ICRYPTO_H_
namespace android {
+namespace hardware {
+class HidlMemory;
+namespace drm {
+namespace V1_0 {
+struct SharedBuffer;
+struct DestinationBuffer;
+} // namespace V1_0
+} // namespace drm
+} // namespace hardware
+} // namespace android
+
+namespace drm = ::android::hardware::drm;
+using drm::V1_0::SharedBuffer;
+
+namespace android {
struct AString;
-class IMemory;
-class IMemoryHeap;
-struct ICrypto : public IInterface {
- DECLARE_META_INTERFACE(Crypto);
+struct ICrypto : public RefBase {
+
+ virtual ~ICrypto() {}
virtual status_t initCheck() const = 0;
@@ -48,27 +63,16 @@
virtual status_t setMediaDrmSession(const Vector<uint8_t> &sessionId) = 0;
- struct SourceBuffer {
- sp<IMemory> mSharedMemory;
- int32_t mHeapSeqNum;
- };
-
enum DestinationType {
kDestinationTypeSharedMemory, // non-secure
kDestinationTypeNativeHandle // secure
};
- struct DestinationBuffer {
- DestinationType mType;
- native_handle_t *mHandle;
- sp<IMemory> mSharedMemory;
- };
-
- virtual ssize_t decrypt(const uint8_t key[16], const uint8_t iv[16],
- CryptoPlugin::Mode mode, const CryptoPlugin::Pattern &pattern,
- const SourceBuffer &source, size_t offset,
- const CryptoPlugin::SubSample *subSamples, size_t numSubSamples,
- const DestinationBuffer &destination, AString *errorDetailMsg) = 0;
+ virtual ssize_t decrypt(const uint8_t /*key*/[16], const uint8_t /*iv*/[16],
+ CryptoPlugin::Mode /*mode*/, const CryptoPlugin::Pattern &/*pattern*/,
+ const drm::V1_0::SharedBuffer &/*source*/, size_t /*offset*/,
+ const CryptoPlugin::SubSample * /*subSamples*/, size_t /*numSubSamples*/,
+ const drm::V1_0::DestinationBuffer &/*destination*/, AString * /*errorDetailMsg*/) = 0;
/**
* Declare the heap that the shared memory source buffers passed
@@ -76,22 +80,16 @@
* that subsequent decrypt calls can use to refer to the heap,
* with -1 indicating failure.
*/
- virtual int32_t setHeap(const sp<IMemoryHeap>& heap) = 0;
+ virtual int32_t setHeap(const sp<hardware::HidlMemory>& heap) = 0;
virtual void unsetHeap(int32_t seqNum) = 0;
+protected:
+ ICrypto() {}
+
private:
DISALLOW_EVIL_CONSTRUCTORS(ICrypto);
};
-struct BnCrypto : public BnInterface<ICrypto> {
- virtual status_t onTransact(
- uint32_t code, const Parcel &data, Parcel *reply,
- uint32_t flags = 0);
-private:
- void readVector(const Parcel &data, Vector<uint8_t> &vector) const;
- void writeVector(Parcel *reply, Vector<uint8_t> const &vector) const;
-};
-
} // namespace android
#endif // ANDROID_ICRYPTO_H_
diff --git a/drm/libmediadrm/tests/Android.bp b/drm/libmediadrm/tests/Android.bp
index 2e39943..7471e05 100644
--- a/drm/libmediadrm/tests/Android.bp
+++ b/drm/libmediadrm/tests/Android.bp
@@ -24,6 +24,8 @@
"libbinder",
"libhidlbase",
"liblog",
+ "libmediadrm",
+ "libmediadrmmetrics_consumer",
"libmediadrmmetrics_full",
"libmediametrics",
"libprotobuf-cpp-full",
diff --git a/drm/libmediadrm/tests/DrmMetrics_test.cpp b/drm/libmediadrm/tests/DrmMetrics_test.cpp
index 5c8a1b0..f362d60 100644
--- a/drm/libmediadrm/tests/DrmMetrics_test.cpp
+++ b/drm/libmediadrm/tests/DrmMetrics_test.cpp
@@ -16,6 +16,7 @@
#define LOG_TAG "DrmMetricsTest"
#include "mediadrm/DrmMetrics.h"
+#include "mediadrm/DrmMetricsConsumer.h"
#include <android/hardware/drm/1.0/types.h>
#include <android/hardware/drm/1.1/types.h>
@@ -58,8 +59,9 @@
TEST_F(MediaDrmMetricsTest, EmptySuccess) {
MediaDrmMetrics metrics;
PersistableBundle bundle;
+ DrmMetricsConsumer consumer(&bundle);
- metrics.Export(&bundle);
+ consumer.consumeFrameworkMetrics(metrics);
EXPECT_TRUE(bundle.empty());
}
@@ -85,8 +87,9 @@
metrics.mEventCounter.Increment(EventType::PROVISION_REQUIRED);
PersistableBundle bundle;
+ DrmMetricsConsumer consumer(&bundle);
- metrics.Export(&bundle);
+ consumer.consumeFrameworkMetrics(metrics);
EXPECT_EQ(11U, bundle.size());
// Verify the list of pairs of int64 metrics.
@@ -174,7 +177,8 @@
metrics.SetSessionEnd(sessionId1);
PersistableBundle bundle;
- metrics.Export(&bundle);
+ DrmMetricsConsumer consumer(&bundle);
+ consumer.consumeFrameworkMetrics(metrics);
EXPECT_EQ(35U, bundle.size());
// Verify the list of pairs of int64 metrics.
@@ -421,7 +425,7 @@
hidl_vec<DrmMetricGroup> hidlMetricGroups;
PersistableBundle bundleMetricGroups;
- ASSERT_EQ(OK, MediaDrmMetrics::HidlMetricsToBundle(hidlMetricGroups, &bundleMetricGroups));
+ ASSERT_EQ(OK, DrmMetricsConsumer::HidlMetricsToBundle(hidlMetricGroups, &bundleMetricGroups));
ASSERT_EQ(0U, bundleMetricGroups.size());
}
@@ -441,7 +445,7 @@
} } };
PersistableBundle bundleMetricGroups;
- ASSERT_EQ(OK, MediaDrmMetrics::HidlMetricsToBundle(hidl_vec<DrmMetricGroup>({hidlMetricGroup}),
+ ASSERT_EQ(OK, DrmMetricsConsumer::HidlMetricsToBundle(hidl_vec<DrmMetricGroup>({hidlMetricGroup}),
&bundleMetricGroups));
ASSERT_EQ(1U, bundleMetricGroups.size());
PersistableBundle bundleMetricGroup;
diff --git a/drm/mediadrm/plugins/TEST_MAPPING b/drm/mediadrm/plugins/TEST_MAPPING
new file mode 100644
index 0000000..7bd1568
--- /dev/null
+++ b/drm/mediadrm/plugins/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsMediaTestCases",
+ "options" : [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "include-filter": "android.media.cts.MediaDrmClearkeyTest"
+ },
+ {
+ "include-filter": "android.media.cts.MediaDrmMetricsTest"
+ }
+ ]
+ }
+ ]
+}
diff --git a/drm/mediadrm/plugins/clearkey/common/ClearKeyUUID.cpp b/drm/mediadrm/plugins/clearkey/common/ClearKeyUUID.cpp
index 0259a42..4e7daec 100644
--- a/drm/mediadrm/plugins/clearkey/common/ClearKeyUUID.cpp
+++ b/drm/mediadrm/plugins/clearkey/common/ClearKeyUUID.cpp
@@ -20,20 +20,28 @@
namespace clearkeydrm {
+namespace {
+
+const std::array<uint8_t, 16> kCommonPsshBoxUUID{
+ 0x10,0x77,0xEF,0xEC,0xC0,0xB2,0x4D,0x02,
+ 0xAC,0xE3,0x3C,0x1E,0x52,0xE2,0xFB,0x4B
+};
+
+// To be used in mpd to specify drm scheme for players
+const std::array<uint8_t, 16> kClearKeyUUID{
+ 0xE2,0x71,0x9D,0x58,0xA9,0x85,0xB3,0xC9,
+ 0x78,0x1A,0xB0,0x30,0xAF,0x78,0xD3,0x0E
+};
+
+}
+
bool isClearKeyUUID(const uint8_t uuid[16]) {
- static const uint8_t kCommonPsshBoxUUID[16] = {
- 0x10,0x77,0xEF,0xEC,0xC0,0xB2,0x4D,0x02,
- 0xAC,0xE3,0x3C,0x1E,0x52,0xE2,0xFB,0x4B
- };
+ return !memcmp(uuid, kCommonPsshBoxUUID.data(), kCommonPsshBoxUUID.size()) ||
+ !memcmp(uuid, kClearKeyUUID.data(), kClearKeyUUID.size());
+}
- // To be used in mpd to specify drm scheme for players
- static const uint8_t kClearKeyUUID[16] = {
- 0xE2,0x71,0x9D,0x58,0xA9,0x85,0xB3,0xC9,
- 0x78,0x1A,0xB0,0x30,0xAF,0x78,0xD3,0x0E
- };
-
- return !memcmp(uuid, kCommonPsshBoxUUID, sizeof(kCommonPsshBoxUUID)) ||
- !memcmp(uuid, kClearKeyUUID, sizeof(kClearKeyUUID));
+std::vector<std::array<uint8_t, 16>> getSupportedCryptoSchemes() {
+ return {kCommonPsshBoxUUID, kClearKeyUUID};
}
} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/common/include/ClearKeyUUID.h b/drm/mediadrm/plugins/clearkey/common/include/ClearKeyUUID.h
index ac99418..fe10fba 100644
--- a/drm/mediadrm/plugins/clearkey/common/include/ClearKeyUUID.h
+++ b/drm/mediadrm/plugins/clearkey/common/include/ClearKeyUUID.h
@@ -17,12 +17,16 @@
#ifndef CLEARKEY_UUID_H_
#define CLEARKEY_UUID_H_
-#include <stdint.h>
+#include <array>
+#include <cstdint>
+#include <vector>
namespace clearkeydrm {
bool isClearKeyUUID(const uint8_t uuid[16]);
+std::vector<std::array<uint8_t, 16>> getSupportedCryptoSchemes();
+
} // namespace clearkeydrm
#endif // CLEARKEY_UUID_H_
diff --git a/drm/mediadrm/plugins/clearkey/default/InitDataParser.cpp b/drm/mediadrm/plugins/clearkey/default/InitDataParser.cpp
index caff393..121a4e2 100644
--- a/drm/mediadrm/plugins/clearkey/default/InitDataParser.cpp
+++ b/drm/mediadrm/plugins/clearkey/default/InitDataParser.cpp
@@ -76,10 +76,21 @@
android::status_t InitDataParser::parsePssh(const Vector<uint8_t>& initData,
Vector<const uint8_t*>* keyIds) {
+ // Description of PSSH format:
+ // https://w3c.github.io/encrypted-media/format-registry/initdata/cenc.html
size_t readPosition = 0;
- // Validate size field
uint32_t expectedSize = initData.size();
+ const char psshIdentifier[4] = {'p', 's', 's', 'h'};
+ const uint8_t psshVersion1[4] = {1, 0, 0, 0};
+ uint32_t keyIdCount = 0;
+ size_t headerSize = sizeof(expectedSize) + sizeof(psshIdentifier) +
+ sizeof(psshVersion1) + kSystemIdSize + sizeof(keyIdCount);
+ if (initData.size() < headerSize) {
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ // Validate size field
expectedSize = htonl(expectedSize);
if (memcmp(&initData[readPosition], &expectedSize,
sizeof(expectedSize)) != 0) {
@@ -88,7 +99,6 @@
readPosition += sizeof(expectedSize);
// Validate PSSH box identifier
- const char psshIdentifier[4] = {'p', 's', 's', 'h'};
if (memcmp(&initData[readPosition], psshIdentifier,
sizeof(psshIdentifier)) != 0) {
return android::ERROR_DRM_CANNOT_HANDLE;
@@ -96,7 +106,6 @@
readPosition += sizeof(psshIdentifier);
// Validate EME version number
- const uint8_t psshVersion1[4] = {1, 0, 0, 0};
if (memcmp(&initData[readPosition], psshVersion1,
sizeof(psshVersion1)) != 0) {
return android::ERROR_DRM_CANNOT_HANDLE;
@@ -110,12 +119,14 @@
readPosition += kSystemIdSize;
// Read key ID count
- uint32_t keyIdCount;
memcpy(&keyIdCount, &initData[readPosition], sizeof(keyIdCount));
keyIdCount = ntohl(keyIdCount);
readPosition += sizeof(keyIdCount);
- if (readPosition + ((uint64_t)keyIdCount * kKeyIdSize) !=
- initData.size() - sizeof(uint32_t)) {
+
+ uint64_t psshSize = 0;
+ if (__builtin_mul_overflow(keyIdCount, kKeyIdSize, &psshSize) ||
+ __builtin_add_overflow(readPosition, psshSize, &psshSize) ||
+ psshSize != initData.size() - sizeof(uint32_t) /* DataSize(0) */) {
return android::ERROR_DRM_CANNOT_HANDLE;
}
diff --git a/drm/mediadrm/plugins/clearkey/hidl/Android.bp b/drm/mediadrm/plugins/clearkey/hidl/Android.bp
index a153ce2..a194416 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/Android.bp
+++ b/drm/mediadrm/plugins/clearkey/hidl/Android.bp
@@ -43,6 +43,7 @@
"android.hardware.drm@1.0",
"android.hardware.drm@1.1",
"android.hardware.drm@1.2",
+ "android.hardware.drm@1.3",
"libbase",
"libbinder",
"libcrypto",
@@ -77,16 +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.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/CreatePluginFactories.cpp b/drm/mediadrm/plugins/clearkey/hidl/CreatePluginFactories.cpp
index 1410d77..bfb0e05 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/CreatePluginFactories.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/CreatePluginFactories.cpp
@@ -22,7 +22,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_3 {
namespace clearkey {
extern "C" {
@@ -38,7 +38,7 @@
} // extern "C"
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_3
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/CryptoFactory.cpp b/drm/mediadrm/plugins/clearkey/hidl/CryptoFactory.cpp
index 2a48db6..a6ed3bd 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/CryptoFactory.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/CryptoFactory.cpp
@@ -27,9 +27,12 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_3 {
namespace clearkey {
+using ::android::hardware::drm::V1_0::Status;
+using ::android::hardware::drm::V1_2::clearkey::CryptoPlugin;
+
Return<bool> CryptoFactory::isCryptoSchemeSupported(
const hidl_array<uint8_t, 16> &uuid)
{
@@ -60,7 +63,7 @@
}
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_3
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp
index 9fb5bbe..ccc73b6 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp
@@ -15,6 +15,7 @@
*/
//#define LOG_NDEBUG 0
+#include <vector>
#define LOG_TAG "hidl_ClearKeyDrmFactory"
#include <utils/Log.h>
@@ -30,11 +31,13 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_3 {
namespace clearkey {
using ::android::hardware::drm::V1_0::Status;
using ::android::hardware::drm::V1_1::SecurityLevel;
+using ::android::hardware::drm::V1_2::clearkey::DrmPlugin;
+using ::android::hardware::drm::V1_2::clearkey::SessionLibrary;
using ::android::hardware::Void;
Return<bool> DrmFactory::isCryptoSchemeSupported(
@@ -78,8 +81,18 @@
return Void();
}
+Return<void> DrmFactory::getSupportedCryptoSchemes(
+ getSupportedCryptoSchemes_cb _hidl_cb) {
+ std::vector<hidl_array<uint8_t, 16>> schemes;
+ for (const auto &scheme : clearkeydrm::getSupportedCryptoSchemes()) {
+ schemes.push_back(scheme);
+ }
+ _hidl_cb(schemes);
+ return Void();
+}
+
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_3
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
index d74bc53..942ea7d 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
@@ -111,6 +111,8 @@
// The content in this secure stop is implementation dependent, the clearkey
// secureStop does not serve as a reference implementation.
void DrmPlugin::installSecureStop(const hidl_vec<uint8_t>& sessionId) {
+ Mutex::Autolock lock(mSecureStopLock);
+
ClearkeySecureStop clearkeySecureStop;
clearkeySecureStop.id = uint32ToVector(++mNextSecureStopId);
clearkeySecureStop.data.assign(sessionId.begin(), sessionId.end());
@@ -744,6 +746,7 @@
}
Return<void> DrmPlugin::getSecureStops(getSecureStops_cb _hidl_cb) {
+ mSecureStopLock.lock();
std::vector<SecureStop> stops;
for (auto itr = mSecureStops.begin(); itr != mSecureStops.end(); ++itr) {
ClearkeySecureStop clearkeyStop = itr->second;
@@ -755,26 +758,32 @@
stop.opaqueData = toHidlVec(stopVec);
stops.push_back(stop);
}
+ mSecureStopLock.unlock();
+
_hidl_cb(Status::OK, stops);
return Void();
}
Return<void> DrmPlugin::getSecureStop(const hidl_vec<uint8_t>& secureStopId,
getSecureStop_cb _hidl_cb) {
- SecureStop stop;
+ std::vector<uint8_t> stopVec;
+
+ mSecureStopLock.lock();
auto itr = mSecureStops.find(toVector(secureStopId));
if (itr != mSecureStops.end()) {
ClearkeySecureStop clearkeyStop = itr->second;
- std::vector<uint8_t> stopVec;
stopVec.insert(stopVec.end(), clearkeyStop.id.begin(), clearkeyStop.id.end());
stopVec.insert(stopVec.end(), clearkeyStop.data.begin(), clearkeyStop.data.end());
+ }
+ mSecureStopLock.unlock();
+ SecureStop stop;
+ if (!stopVec.empty()) {
stop.opaqueData = toHidlVec(stopVec);
_hidl_cb(Status::OK, stop);
} else {
_hidl_cb(Status::BAD_VALUE, stop);
}
-
return Void();
}
@@ -787,51 +796,73 @@
}
Return<void> DrmPlugin::getSecureStopIds(getSecureStopIds_cb _hidl_cb) {
+ mSecureStopLock.lock();
std::vector<SecureStopId> ids;
for (auto itr = mSecureStops.begin(); itr != mSecureStops.end(); ++itr) {
ids.push_back(itr->first);
}
+ mSecureStopLock.unlock();
_hidl_cb(Status::OK, toHidlVec(ids));
return Void();
}
Return<Status> DrmPlugin::releaseSecureStops(const SecureStopRelease& ssRelease) {
- if (ssRelease.opaqueData.size() == 0) {
+ // OpaqueData starts with 4 byte decimal integer string
+ const size_t kFourBytesOffset = 4;
+ if (ssRelease.opaqueData.size() < kFourBytesOffset) {
+ ALOGE("Invalid secureStopRelease length");
return Status::BAD_VALUE;
}
Status status = Status::OK;
std::vector<uint8_t> input = toVector(ssRelease.opaqueData);
+ if (input.size() < kSecureStopIdSize + kFourBytesOffset) {
+ // The minimum size of SecureStopRelease has to contain
+ // a 4 bytes count and one secureStop id
+ ALOGE("Total size of secureStops is too short");
+ return Status::BAD_VALUE;
+ }
+
// The format of opaqueData is shared between the server
// 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);
uint32_t count = 0;
sscanf(reinterpret_cast<char*>(input.data()), "%04" PRIu32, &count);
// Avoid divide by 0 below.
if (count == 0) {
+ ALOGE("Invalid 0 secureStop count");
return Status::BAD_VALUE;
}
- size_t secureStopSize = (input.size() - countBufferSize) / count;
- uint8_t buffer[secureStopSize];
- size_t offset = countBufferSize; // skip the count
+ // Computes the fixed length secureStop size
+ size_t secureStopSize = (input.size() - kFourBytesOffset) / count;
+ if (secureStopSize < kSecureStopIdSize) {
+ // A valid secureStop contains the id plus data
+ ALOGE("Invalid secureStop size");
+ return Status::BAD_VALUE;
+ }
+ uint8_t* buffer = new uint8_t[secureStopSize];
+ size_t offset = kFourBytesOffset; // skip the count
for (size_t i = 0; i < count; ++i, offset += secureStopSize) {
memcpy(buffer, input.data() + offset, secureStopSize);
- std::vector<uint8_t> id(buffer, buffer + kSecureStopIdSize);
+ // A secureStop contains id+data, we only use the id for removal
+ std::vector<uint8_t> id(buffer, buffer + kSecureStopIdSize);
status = removeSecureStop(toHidlVec(id));
if (Status::OK != status) break;
}
+ delete[] buffer;
return status;
}
Return<Status> DrmPlugin::removeSecureStop(const hidl_vec<uint8_t>& secureStopId) {
+ Mutex::Autolock lock(mSecureStopLock);
+
if (1 != mSecureStops.erase(toVector(secureStopId))) {
return Status::BAD_VALUE;
}
@@ -839,6 +870,8 @@
}
Return<Status> DrmPlugin::removeAllSecureStops() {
+ Mutex::Autolock lock(mSecureStopLock);
+
mSecureStops.clear();
mNextSecureStopId = kSecureStopIdStart;
return Status::OK;
diff --git a/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp b/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp
index b988ce0..8513434 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp
@@ -85,10 +85,21 @@
Status InitDataParser::parsePssh(const std::vector<uint8_t>& initData,
std::vector<const uint8_t*>* keyIds) {
+ // Description of PSSH format:
+ // https://w3c.github.io/encrypted-media/format-registry/initdata/cenc.html
size_t readPosition = 0;
- // Validate size field
uint32_t expectedSize = initData.size();
+ const char psshIdentifier[4] = {'p', 's', 's', 'h'};
+ const uint8_t psshVersion1[4] = {1, 0, 0, 0};
+ uint32_t keyIdCount = 0;
+ size_t headerSize = sizeof(expectedSize) + sizeof(psshIdentifier) +
+ sizeof(psshVersion1) + kSystemIdSize + sizeof(keyIdCount);
+ if (initData.size() < headerSize) {
+ return Status::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ // Validate size field
expectedSize = htonl(expectedSize);
if (memcmp(&initData[readPosition], &expectedSize,
sizeof(expectedSize)) != 0) {
@@ -97,7 +108,6 @@
readPosition += sizeof(expectedSize);
// Validate PSSH box identifier
- const char psshIdentifier[4] = {'p', 's', 's', 'h'};
if (memcmp(&initData[readPosition], psshIdentifier,
sizeof(psshIdentifier)) != 0) {
return Status::ERROR_DRM_CANNOT_HANDLE;
@@ -105,7 +115,6 @@
readPosition += sizeof(psshIdentifier);
// Validate EME version number
- const uint8_t psshVersion1[4] = {1, 0, 0, 0};
if (memcmp(&initData[readPosition], psshVersion1,
sizeof(psshVersion1)) != 0) {
return Status::ERROR_DRM_CANNOT_HANDLE;
@@ -119,12 +128,14 @@
readPosition += kSystemIdSize;
// Read key ID count
- uint32_t keyIdCount;
memcpy(&keyIdCount, &initData[readPosition], sizeof(keyIdCount));
keyIdCount = ntohl(keyIdCount);
readPosition += sizeof(keyIdCount);
- if (readPosition + ((uint64_t)keyIdCount * kKeyIdSize) !=
- initData.size() - sizeof(uint32_t)) {
+
+ uint64_t psshSize = 0;
+ if (__builtin_mul_overflow(keyIdCount, kKeyIdSize, &psshSize) ||
+ __builtin_add_overflow(readPosition, psshSize, &psshSize) ||
+ psshSize != initData.size() - sizeof(uint32_t) /* DataSize(0) */) {
return Status::ERROR_DRM_CANNOT_HANDLE;
}
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 5ba669d..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,6 +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
+ 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/include/CreatePluginFactories.h b/drm/mediadrm/plugins/clearkey/hidl/include/CreatePluginFactories.h
index 6368f3d..c1c188e 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/CreatePluginFactories.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/CreatePluginFactories.h
@@ -17,17 +17,17 @@
#ifndef CLEARKEY_CREATE_PLUGIN_FACTORIES_H_
#define CLEARKEY_CREATE_PLUGIN_FACTORIES_H_
-#include <android/hardware/drm/1.2/ICryptoFactory.h>
-#include <android/hardware/drm/1.2/IDrmFactory.h>
+#include <android/hardware/drm/1.3/ICryptoFactory.h>
+#include <android/hardware/drm/1.3/IDrmFactory.h>
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_3 {
namespace clearkey {
-using ::android::hardware::drm::V1_2::ICryptoFactory;
-using ::android::hardware::drm::V1_2::IDrmFactory;
+using ::android::hardware::drm::V1_3::ICryptoFactory;
+using ::android::hardware::drm::V1_3::IDrmFactory;
extern "C" {
IDrmFactory* createDrmFactory();
@@ -35,7 +35,7 @@
}
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_3
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/CryptoFactory.h b/drm/mediadrm/plugins/clearkey/hidl/include/CryptoFactory.h
index 203bb2d..cb4811b 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/CryptoFactory.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/CryptoFactory.h
@@ -18,17 +18,17 @@
#define CLEARKEY_CRYPTO_FACTORY_H_
#include <android/hardware/drm/1.0/ICryptoPlugin.h>
-#include <android/hardware/drm/1.2/ICryptoFactory.h>
+#include <android/hardware/drm/1.3/ICryptoFactory.h>
#include "ClearKeyTypes.h"
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_3 {
namespace clearkey {
-using ::android::hardware::drm::V1_2::ICryptoFactory;
+using ::android::hardware::drm::V1_3::ICryptoFactory;
using ::android::hardware::drm::V1_0::ICryptoPlugin;
using ::android::hardware::hidl_array;
using ::android::hardware::hidl_string;
@@ -52,7 +52,7 @@
};
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_3
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h b/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h
index 4ca856d..403a8ec 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h
@@ -18,16 +18,17 @@
#define CLEARKEY_DRM_FACTORY_H_
#include <android/hardware/drm/1.2/IDrmPlugin.h>
-#include <android/hardware/drm/1.2/IDrmFactory.h>
+#include <android/hardware/drm/1.3/IDrmFactory.h>
#include "ClearKeyTypes.h"
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_3 {
namespace clearkey {
+using ::android::hardware::drm::V1_1::SecurityLevel;
using ::android::hardware::hidl_array;
using ::android::hardware::hidl_string;
using ::android::hardware::Return;
@@ -51,12 +52,15 @@
const hidl_string& appPackageName,
createPlugin_cb _hidl_cb) override;
+ Return<void> getSupportedCryptoSchemes(
+ getSupportedCryptoSchemes_cb _hidl_cb) override;
+
private:
CLEARKEY_DISALLOW_COPY_AND_ASSIGN(DrmFactory);
};
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_3
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
index f294d4d..3de7589 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
@@ -416,6 +416,7 @@
}
DeviceFiles mFileHandle;
+ Mutex mSecureStopLock;
CLEARKEY_DISALLOW_COPY_AND_ASSIGN_AND_NEW(DrmPlugin);
};
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/drm/mediadrm/plugins/clearkey/hidl/manifest_android.hardware.drm@1.3-service.clearkey.xml b/drm/mediadrm/plugins/clearkey/hidl/manifest_android.hardware.drm@1.3-service.clearkey.xml
new file mode 100644
index 0000000..229ee96
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/hidl/manifest_android.hardware.drm@1.3-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.3::ICryptoFactory/clearkey</fqname>
+ <fqname>@1.3::IDrmFactory/clearkey</fqname>
+ </hal>
+</manifest>
diff --git a/drm/mediadrm/plugins/clearkey/hidl/service.cpp b/drm/mediadrm/plugins/clearkey/hidl/service.cpp
index b39ea01..b62baae 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/service.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/service.cpp
@@ -25,10 +25,10 @@
using ::android::hardware::joinRpcThreadpool;
using ::android::sp;
-using android::hardware::drm::V1_2::ICryptoFactory;
-using android::hardware::drm::V1_2::IDrmFactory;
-using android::hardware::drm::V1_2::clearkey::CryptoFactory;
-using android::hardware::drm::V1_2::clearkey::DrmFactory;
+using android::hardware::drm::V1_3::ICryptoFactory;
+using android::hardware::drm::V1_3::IDrmFactory;
+using android::hardware::drm::V1_3::clearkey::CryptoFactory;
+using android::hardware::drm::V1_3::clearkey::DrmFactory;
int main(int /* argc */, char** /* argv */) {
sp<IDrmFactory> drmFactory = new DrmFactory;
diff --git a/drm/mediadrm/plugins/clearkey/hidl/serviceLazy.cpp b/drm/mediadrm/plugins/clearkey/hidl/serviceLazy.cpp
index a510487..b3e0179 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/serviceLazy.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/serviceLazy.cpp
@@ -25,10 +25,10 @@
using ::android::hardware::joinRpcThreadpool;
using ::android::sp;
-using android::hardware::drm::V1_2::ICryptoFactory;
-using android::hardware::drm::V1_2::IDrmFactory;
-using android::hardware::drm::V1_2::clearkey::CryptoFactory;
-using android::hardware::drm::V1_2::clearkey::DrmFactory;
+using android::hardware::drm::V1_3::ICryptoFactory;
+using android::hardware::drm::V1_3::IDrmFactory;
+using android::hardware::drm::V1_3::clearkey::CryptoFactory;
+using android::hardware::drm::V1_3::clearkey::DrmFactory;
using android::hardware::LazyServiceRegistrar;
int main(int /* argc */, char** /* argv */) {
diff --git a/include/media/AudioResampler.h b/include/media/AudioResampler.h
deleted file mode 120000
index 771f1b8..0000000
--- a/include/media/AudioResampler.h
+++ /dev/null
@@ -1 +0,0 @@
-../../media/libaudioprocessing/include/media/AudioResampler.h
\ No newline at end of file
diff --git a/include/media/DataSource.h b/include/media/DataSource.h
deleted file mode 120000
index 198b27e..0000000
--- a/include/media/DataSource.h
+++ /dev/null
@@ -1 +0,0 @@
-stagefright/DataSource.h
\ No newline at end of file
diff --git a/media/libstagefright/include/media/stagefright/DataSource.h b/include/media/DataSource.h
similarity index 96%
rename from media/libstagefright/include/media/stagefright/DataSource.h
rename to include/media/DataSource.h
index 83d3e5d..2abd5f5 100644
--- a/media/libstagefright/include/media/stagefright/DataSource.h
+++ b/include/media/DataSource.h
@@ -19,14 +19,14 @@
#define DATA_SOURCE_H_
#include <sys/types.h>
+
+#include <android/IDataSource.h>
#include <media/stagefright/MediaErrors.h>
-#include <media/DataSourceBase.h>
-#include <media/IDataSource.h>
+#include <media/stagefright/DataSourceBase.h>
#include <media/MediaExtractorPluginApi.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/threads.h>
-#include <drm/DrmManagerClient.h>
namespace android {
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/IMediaMetricsService.h b/include/media/IMediaMetricsService.h
new file mode 120000
index 0000000..84b99ee
--- /dev/null
+++ b/include/media/IMediaMetricsService.h
@@ -0,0 +1 @@
+../../media/libmediametrics/include/IMediaMetricsService.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/MediaMetricsItem.h b/include/media/MediaMetricsItem.h
new file mode 120000
index 0000000..5438953
--- /dev/null
+++ b/include/media/MediaMetricsItem.h
@@ -0,0 +1 @@
+../../media/libmediametrics/include/MediaMetricsItem.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/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index 1b1f149..8aec80d 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -575,7 +575,7 @@
class AudioTrackServerProxy : public ServerProxy {
public:
AudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
- size_t frameSize, bool clientInServer = false, uint32_t sampleRate = 0)
+ size_t frameSize, bool clientInServer, uint32_t sampleRate)
: ServerProxy(cblk, buffers, frameCount, frameSize, true /*isOut*/, clientInServer),
mPlaybackRateObserver(&cblk->mPlaybackRateQueue),
mUnderrunCount(0), mUnderrunning(false), mDrained(true) {
@@ -651,7 +651,7 @@
class StaticAudioTrackServerProxy : public AudioTrackServerProxy {
public:
StaticAudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
- size_t frameSize);
+ size_t frameSize, uint32_t sampleRate);
protected:
virtual ~StaticAudioTrackServerProxy() { }
diff --git a/include/private/media/VideoFrame.h b/include/private/media/VideoFrame.h
index 712f118..16e794a 100644
--- a/include/private/media/VideoFrame.h
+++ b/include/private/media/VideoFrame.h
@@ -41,7 +41,7 @@
uint32_t angle, uint32_t bpp, bool hasData, size_t iccSize):
mWidth(width), mHeight(height),
mDisplayWidth(displayWidth), mDisplayHeight(displayHeight),
- mTileWidth(tileWidth), mTileHeight(tileHeight),
+ mTileWidth(tileWidth), mTileHeight(tileHeight), mDurationUs(0),
mRotationAngle(angle), mBytesPerPixel(bpp), mRowBytes(bpp * width),
mSize(hasData ? (bpp * width * height) : 0),
mIccSize(iccSize), mReserved(0) {
@@ -78,6 +78,7 @@
uint32_t mDisplayHeight; // Display height before rotation
uint32_t mTileWidth; // Tile width (0 if image doesn't have grid)
uint32_t mTileHeight; // Tile height (0 if image doesn't have grid)
+ int64_t mDurationUs; // Frame duration in microseconds
int32_t mRotationAngle; // Rotation angle, clockwise, should be multiple of 90
uint32_t mBytesPerPixel; // Number of bytes per pixel
uint32_t mRowBytes; // Number of bytes per row before rotation
diff --git a/include/soundtrigger/ISoundTrigger.h b/include/soundtrigger/ISoundTrigger.h
deleted file mode 100644
index c357caa..0000000
--- a/include/soundtrigger/ISoundTrigger.h
+++ /dev/null
@@ -1,60 +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;
-
-};
-
-// ----------------------------------------------------------------------------
-
-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 ccc61dc..0000000
--- a/include/soundtrigger/SoundTrigger.h
+++ /dev/null
@@ -1,83 +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);
-
- // 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..79b77a0 100644
--- a/media/audioserver/Android.mk
+++ b/media/audioserver/Android.mk
@@ -19,10 +19,12 @@
libmediautils \
libnbaio \
libnblog \
- libsoundtriggerservice \
libutils \
libvibrator
+LOCAL_HEADER_LIBRARIES := \
+ libaudiohal_headers \
+
# TODO oboeservice is the old folder name for aaudioservice. It will be changed.
LOCAL_C_INCLUDES := \
frameworks/av/services/audioflinger \
@@ -33,7 +35,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/1.0/AccessorImpl.cpp b/media/bufferpool/1.0/AccessorImpl.cpp
index a5366f6..0d7e92f 100644
--- a/media/bufferpool/1.0/AccessorImpl.cpp
+++ b/media/bufferpool/1.0/AccessorImpl.cpp
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-#define LOG_TAG "BufferPoolAccessor"
+#define LOG_TAG "BufferPoolAccessor1.0"
//#define LOG_NDEBUG 0
#include <sys/types.h>
+#include <stdint.h>
#include <time.h>
#include <unistd.h>
#include <utils/Log.h>
@@ -127,7 +128,6 @@
return false;
}
-int32_t Accessor::Impl::sPid = getpid();
uint32_t Accessor::Impl::sSeqId = time(nullptr);
Accessor::Impl::Impl(
@@ -145,14 +145,19 @@
{
std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
if (newConnection) {
- ConnectionId id = (int64_t)sPid << 32 | sSeqId;
+ int32_t pid = getpid();
+ ConnectionId id = (int64_t)pid << 32 | sSeqId;
status = mBufferPool.mObserver.open(id, fmqDescPtr);
if (status == ResultStatus::OK) {
newConnection->initialize(accessor, id);
*connection = newConnection;
*pConnectionId = id;
mBufferPool.mConnectionIds.insert(id);
- ++sSeqId;
+ if (sSeqId == UINT32_MAX) {
+ sSeqId = 0;
+ } else {
+ ++sSeqId;
+ }
}
}
mBufferPool.processStatusMessages();
@@ -248,7 +253,7 @@
ALOGD("Destruction - bufferpool %p "
"cached: %zu/%zuM, %zu/%d%% in use; "
"allocs: %zu, %d%% recycled; "
- "transfers: %zu, %d%% unfetced",
+ "transfers: %zu, %d%% unfetched",
this, mStats.mBuffersCached, mStats.mSizeCached >> 20,
mStats.mBuffersInUse, percentage(mStats.mBuffersInUse, mStats.mBuffersCached),
mStats.mTotalAllocations, percentage(mStats.mTotalRecycles, mStats.mTotalAllocations),
diff --git a/media/bufferpool/1.0/AccessorImpl.h b/media/bufferpool/1.0/AccessorImpl.h
index 84cb685..a09cbe2 100644
--- a/media/bufferpool/1.0/AccessorImpl.h
+++ b/media/bufferpool/1.0/AccessorImpl.h
@@ -61,7 +61,6 @@
// ConnectionId = pid : (timestamp_created + seqId)
// in order to guarantee uniqueness for each connection
static uint32_t sSeqId;
- static int32_t sPid;
const std::shared_ptr<BufferPoolAllocator> mAllocator;
diff --git a/media/bufferpool/2.0/AccessorImpl.cpp b/media/bufferpool/2.0/AccessorImpl.cpp
index 0d591d7..1947656 100644
--- a/media/bufferpool/2.0/AccessorImpl.cpp
+++ b/media/bufferpool/2.0/AccessorImpl.cpp
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-#define LOG_TAG "BufferPoolAccessor"
+#define LOG_TAG "BufferPoolAccessor2.0"
//#define LOG_NDEBUG 0
#include <sys/types.h>
+#include <stdint.h>
#include <time.h>
#include <unistd.h>
#include <utils/Log.h>
@@ -134,7 +135,6 @@
return false;
}
-int32_t Accessor::Impl::sPid = getpid();
uint32_t Accessor::Impl::sSeqId = time(nullptr);
Accessor::Impl::Impl(
@@ -156,7 +156,8 @@
{
std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
if (newConnection) {
- ConnectionId id = (int64_t)sPid << 32 | sSeqId;
+ int32_t pid = getpid();
+ ConnectionId id = (int64_t)pid << 32 | sSeqId;
status = mBufferPool.mObserver.open(id, statusDescPtr);
if (status == ResultStatus::OK) {
newConnection->initialize(accessor, id);
@@ -166,7 +167,11 @@
mBufferPool.mConnectionIds.insert(id);
mBufferPool.mInvalidationChannel.getDesc(invDescPtr);
mBufferPool.mInvalidation.onConnect(id, observer);
- ++sSeqId;
+ if (sSeqId == UINT32_MAX) {
+ sSeqId = 0;
+ } else {
+ ++sSeqId;
+ }
}
}
@@ -304,7 +309,7 @@
ALOGD("Destruction - bufferpool2 %p "
"cached: %zu/%zuM, %zu/%d%% in use; "
"allocs: %zu, %d%% recycled; "
- "transfers: %zu, %d%% unfetced",
+ "transfers: %zu, %d%% unfetched",
this, mStats.mBuffersCached, mStats.mSizeCached >> 20,
mStats.mBuffersInUse, percentage(mStats.mBuffersInUse, mStats.mBuffersCached),
mStats.mTotalAllocations, percentage(mStats.mTotalRecycles, mStats.mTotalAllocations),
diff --git a/media/bufferpool/2.0/AccessorImpl.h b/media/bufferpool/2.0/AccessorImpl.h
index 807e0f1..9888be5 100644
--- a/media/bufferpool/2.0/AccessorImpl.h
+++ b/media/bufferpool/2.0/AccessorImpl.h
@@ -75,7 +75,6 @@
// ConnectionId = pid : (timestamp_created + seqId)
// in order to guarantee uniqueness for each connection
static uint32_t sSeqId;
- static int32_t sPid;
const std::shared_ptr<BufferPoolAllocator> mAllocator;
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/components/aac/C2SoftAacDec.cpp b/media/codec2/components/aac/C2SoftAacDec.cpp
index 2d4e126..3568f7b 100644
--- a/media/codec2/components/aac/C2SoftAacDec.cpp
+++ b/media/codec2/components/aac/C2SoftAacDec.cpp
@@ -78,7 +78,8 @@
DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE)
.withDefault(new C2StreamSampleRateInfo::output(0u, 44100))
.withFields({C2F(mSampleRate, value).oneOf({
- 7350, 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+ 7350, 8000, 11025, 12000, 16000, 22050, 24000, 32000,
+ 44100, 48000, 64000, 88200, 96000
})})
.withSetter(Setter<decltype(*mSampleRate)>::NonStrictValueWithNoDeps)
.build());
diff --git a/media/codec2/components/aom/C2SoftAomDec.cpp b/media/codec2/components/aom/C2SoftAomDec.cpp
index 36137e6..c7046cb 100644
--- a/media/codec2/components/aom/C2SoftAomDec.cpp
+++ b/media/codec2/components/aom/C2SoftAomDec.cpp
@@ -29,6 +29,8 @@
namespace android {
+constexpr size_t kMinInputBufferSize = 2 * 1024 * 1024;
+
// codecname set and passed in as a compile flag from Android.bp
constexpr char COMPONENT_NAME[] = CODECNAME;
@@ -112,7 +114,7 @@
addParameter(
DefineParam(mMaxInputSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
.withDefault(
- new C2StreamMaxBufferSizeInfo::input(0u, 320 * 240 * 3 / 4))
+ new C2StreamMaxBufferSizeInfo::input(0u, kMinInputBufferSize))
.withFields({
C2F(mMaxInputSize, value).any(),
})
@@ -192,8 +194,8 @@
const C2P<C2StreamMaxPictureSizeTuning::output>& maxSize) {
(void)mayBlock;
// assume compression ratio of 2
- me.set().value = (((maxSize.v.width + 63) / 64) *
- ((maxSize.v.height + 63) / 64) * 3072);
+ me.set().value = c2_max((((maxSize.v.width + 63) / 64)
+ * ((maxSize.v.height + 63) / 64) * 3072), kMinInputBufferSize);
return C2R::Ok();
}
static C2R DefaultColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsTuning::output> &me) {
diff --git a/media/codec2/components/avc/C2SoftAvcDec.cpp b/media/codec2/components/avc/C2SoftAvcDec.cpp
index 2be51dd..56813c4 100644
--- a/media/codec2/components/avc/C2SoftAvcDec.cpp
+++ b/media/codec2/components/avc/C2SoftAvcDec.cpp
@@ -31,7 +31,7 @@
namespace android {
namespace {
-
+constexpr size_t kMinInputBufferSize = 2 * 1024 * 1024;
constexpr char COMPONENT_NAME[] = "c2.android.avc.decoder";
constexpr uint32_t kDefaultOutputDelay = 8;
constexpr uint32_t kMaxOutputDelay = 16;
@@ -114,7 +114,7 @@
addParameter(
DefineParam(mMaxInputSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
- .withDefault(new C2StreamMaxBufferSizeInfo::input(0u, 320 * 240 * 3 / 4))
+ .withDefault(new C2StreamMaxBufferSizeInfo::input(0u, kMinInputBufferSize))
.withFields({
C2F(mMaxInputSize, value).any(),
})
@@ -227,7 +227,8 @@
const C2P<C2StreamMaxPictureSizeTuning::output> &maxSize) {
(void)mayBlock;
// assume compression ratio of 2
- me.set().value = (((maxSize.v.width + 15) / 16) * ((maxSize.v.height + 15) / 16) * 192);
+ me.set().value = c2_max((((maxSize.v.width + 15) / 16)
+ * ((maxSize.v.height + 15) / 16) * 192), kMinInputBufferSize);
return C2R::Ok();
}
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/flac/Android.bp b/media/codec2/components/flac/Android.bp
index e5eb51d..48cc51b 100644
--- a/media/codec2/components/flac/Android.bp
+++ b/media/codec2/components/flac/Android.bp
@@ -23,8 +23,11 @@
srcs: ["C2SoftFlacEnc.cpp"],
- static_libs: [
+ shared_libs: [
"libaudioutils",
+ ],
+
+ static_libs: [
"libFLAC",
],
}
diff --git a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
index df7b403..e0365fc 100644
--- a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
+++ b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
@@ -29,7 +29,7 @@
#include "impeg2d.h"
namespace android {
-
+constexpr size_t kMinInputBufferSize = 2 * 1024 * 1024;
constexpr char COMPONENT_NAME[] = "c2.android.mpeg2.decoder";
class C2SoftMpeg2Dec::IntfImpl : public SimpleInterface<void>::BaseParams {
@@ -47,6 +47,12 @@
noInputLatency();
noTimeStretch();
+ // TODO: Proper support for reorder depth.
+ addParameter(
+ DefineParam(mActualOutputDelay, C2_PARAMKEY_OUTPUT_DELAY)
+ .withConstValue(new C2PortActualDelayTuning::output(3u))
+ .build());
+
// TODO: output latency and reordering
addParameter(
@@ -93,7 +99,7 @@
addParameter(
DefineParam(mMaxInputSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
- .withDefault(new C2StreamMaxBufferSizeInfo::input(0u, 320 * 240 * 3 / 2))
+ .withDefault(new C2StreamMaxBufferSizeInfo::input(0u, kMinInputBufferSize))
.withFields({
C2F(mMaxInputSize, value).any(),
})
@@ -207,7 +213,8 @@
const C2P<C2StreamMaxPictureSizeTuning::output> &maxSize) {
(void)mayBlock;
// assume compression ratio of 1
- me.set().value = (((maxSize.v.width + 15) / 16) * ((maxSize.v.height + 15) / 16) * 384);
+ me.set().value = c2_max((((maxSize.v.width + 15) / 16)
+ * ((maxSize.v.height + 15) / 16) * 384), kMinInputBufferSize);
return C2R::Ok();
}
diff --git a/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp
index 7e6685e..61b286c 100644
--- a/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp
+++ b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp
@@ -33,7 +33,7 @@
#include "mp4dec_api.h"
namespace android {
-
+constexpr size_t kMinInputBufferSize = 2 * 1024 * 1024;
#ifdef MPEG4
constexpr char COMPONENT_NAME[] = "c2.android.mpeg4.decoder";
#else
@@ -149,11 +149,7 @@
addParameter(
DefineParam(mMaxInputSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
-#ifdef MPEG4
- .withDefault(new C2StreamMaxBufferSizeInfo::input(0u, 1920 * 1088 * 3 / 2))
-#else
- .withDefault(new C2StreamMaxBufferSizeInfo::input(0u, 352 * 288 * 3 / 2))
-#endif
+ .withDefault(new C2StreamMaxBufferSizeInfo::input(0u, kMinInputBufferSize))
.withFields({
C2F(mMaxInputSize, value).any(),
})
@@ -218,7 +214,8 @@
const C2P<C2StreamMaxPictureSizeTuning::output> &maxSize) {
(void)mayBlock;
// assume compression ratio of 1
- me.set().value = (((maxSize.v.width + 15) / 16) * ((maxSize.v.height + 15) / 16) * 384);
+ me.set().value = c2_max((((maxSize.v.width + 15) / 16)
+ * ((maxSize.v.height + 15) / 16) * 384), kMinInputBufferSize);
return C2R::Ok();
}
diff --git a/media/codec2/components/opus/C2SoftOpusDec.cpp b/media/codec2/components/opus/C2SoftOpusDec.cpp
index 6b6974f..b7c1556 100644
--- a/media/codec2/components/opus/C2SoftOpusDec.cpp
+++ b/media/codec2/components/opus/C2SoftOpusDec.cpp
@@ -62,7 +62,7 @@
addParameter(
DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE)
.withDefault(new C2StreamSampleRateInfo::output(0u, 48000))
- .withFields({C2F(mSampleRate, value).equalTo(48000)})
+ .withFields({C2F(mSampleRate, value).inRange(8000, 48000)})
.withSetter((Setter<decltype(*mSampleRate)>::StrictValueWithNoDeps))
.build());
diff --git a/media/codec2/components/vpx/C2SoftVpxDec.cpp b/media/codec2/components/vpx/C2SoftVpxDec.cpp
index a759e8f..c7d73f4 100644
--- a/media/codec2/components/vpx/C2SoftVpxDec.cpp
+++ b/media/codec2/components/vpx/C2SoftVpxDec.cpp
@@ -30,7 +30,7 @@
#include "C2SoftVpxDec.h"
namespace android {
-
+constexpr size_t kMinInputBufferSize = 2 * 1024 * 1024;
#ifdef VP9
constexpr char COMPONENT_NAME[] = "c2.android.vp9.decoder";
#else
@@ -166,7 +166,7 @@
addParameter(
DefineParam(mMaxInputSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
- .withDefault(new C2StreamMaxBufferSizeInfo::input(0u, 320 * 240 * 3 / 4))
+ .withDefault(new C2StreamMaxBufferSizeInfo::input(0u, kMinInputBufferSize))
.withFields({
C2F(mMaxInputSize, value).any(),
})
@@ -244,7 +244,8 @@
const C2P<C2StreamMaxPictureSizeTuning::output> &maxSize) {
(void)mayBlock;
// assume compression ratio of 2
- me.set().value = (((maxSize.v.width + 63) / 64) * ((maxSize.v.height + 63) / 64) * 3072);
+ me.set().value = c2_max((((maxSize.v.width + 63) / 64)
+ * ((maxSize.v.height + 63) / 64) * 3072), kMinInputBufferSize);
return C2R::Ok();
}
diff --git a/media/codec2/components/xaac/C2SoftXaacDec.cpp b/media/codec2/components/xaac/C2SoftXaacDec.cpp
index a3ebadb..951d058 100644
--- a/media/codec2/components/xaac/C2SoftXaacDec.cpp
+++ b/media/codec2/components/xaac/C2SoftXaacDec.cpp
@@ -87,7 +87,8 @@
DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE)
.withDefault(new C2StreamSampleRateInfo::output(0u, 44100))
.withFields({C2F(mSampleRate, value).oneOf({
- 7350, 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+ 7350, 8000, 11025, 12000, 16000, 22050, 24000, 32000,
+ 44100, 48000, 64000, 88200, 96000
})})
.withSetter((Setter<decltype(*mSampleRate)>::StrictValueWithNoDeps))
.build());
@@ -1309,69 +1310,84 @@
&ui_exec_done);
RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_DONE_QUERY");
- if (ui_exec_done != 1) {
- VOID* p_array; // ITTIAM:buffer to handle gain payload
- WORD32 buf_size = 0; // ITTIAM:gain payload length
- WORD32 bit_str_fmt = 1;
- WORD32 gain_stream_flag = 1;
-
- err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
- IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_LEN, &buf_size);
- RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_LEN");
-
- err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
- IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_BUF, &p_array);
- RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_BUF");
-
- if (buf_size > 0) {
- /*Set bitstream_split_format */
- err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
- IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT, &bit_str_fmt);
- RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
-
- memcpy(mDrcInBuf, p_array, buf_size);
- /* Set number of bytes to be processed */
- err_code =
- ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES_BS, 0, &buf_size);
- RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
-
- err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
- IA_DRC_DEC_CONFIG_GAIN_STREAM_FLAG, &gain_stream_flag);
- RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
-
- /* Execute process */
- err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT,
- IA_CMD_TYPE_INIT_CPY_BSF_BUFF, nullptr);
- RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
-
- mMpegDDRCPresent = 1;
- }
- }
-
- /* How much buffer is used in input buffers */
+ int32_t num_preroll = 0;
err_code = ixheaacd_dec_api(mXheaacCodecHandle,
- IA_API_CMD_GET_CURIDX_INPUT_BUF,
- 0,
- bytesConsumed);
- RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_CURIDX_INPUT_BUF");
+ IA_API_CMD_GET_CONFIG_PARAM,
+ IA_ENHAACPLUS_DEC_CONFIG_GET_NUM_PRE_ROLL_FRAMES,
+ &num_preroll);
+ RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GET_NUM_PRE_ROLL_FRAMES");
- /* Get the output bytes */
- err_code = ixheaacd_dec_api(mXheaacCodecHandle,
- IA_API_CMD_GET_OUTPUT_BYTES,
- 0,
- outBytes);
- RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_OUTPUT_BYTES");
+ {
+ int32_t preroll_frame_offset = 0;
- if (mMpegDDRCPresent == 1) {
- memcpy(mDrcInBuf, mOutputBuffer, *outBytes);
- err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES, 0, outBytes);
- RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_INPUT_BYTES");
+ do {
+ if (ui_exec_done != 1) {
+ VOID* p_array; // ITTIAM:buffer to handle gain payload
+ WORD32 buf_size = 0; // ITTIAM:gain payload length
+ WORD32 bit_str_fmt = 1;
+ WORD32 gain_stream_flag = 1;
- err_code =
- ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_EXECUTE, IA_CMD_TYPE_DO_EXECUTE, nullptr);
- RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_DO_EXECUTE");
+ err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
+ IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_LEN, &buf_size);
+ RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_LEN");
- memcpy(mOutputBuffer, mDrcOutBuf, *outBytes);
+ err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
+ IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_BUF, &p_array);
+ RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_BUF");
+
+ if (buf_size > 0) {
+ /*Set bitstream_split_format */
+ err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
+ IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT, &bit_str_fmt);
+ RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+
+ memcpy(mDrcInBuf, p_array, buf_size);
+ /* Set number of bytes to be processed */
+ err_code =
+ ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES_BS, 0, &buf_size);
+ RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+
+ err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
+ IA_DRC_DEC_CONFIG_GAIN_STREAM_FLAG, &gain_stream_flag);
+ RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+
+ /* Execute process */
+ err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT,
+ IA_CMD_TYPE_INIT_CPY_BSF_BUFF, nullptr);
+ RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+
+ mMpegDDRCPresent = 1;
+ }
+ }
+
+ /* How much buffer is used in input buffers */
+ err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+ IA_API_CMD_GET_CURIDX_INPUT_BUF,
+ 0,
+ bytesConsumed);
+ RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_CURIDX_INPUT_BUF");
+
+ /* Get the output bytes */
+ err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+ IA_API_CMD_GET_OUTPUT_BYTES,
+ 0,
+ outBytes);
+ RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_OUTPUT_BYTES");
+
+ if (mMpegDDRCPresent == 1) {
+ memcpy(mDrcInBuf, mOutputBuffer + preroll_frame_offset, *outBytes);
+ preroll_frame_offset += *outBytes;
+ err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES, 0, outBytes);
+ RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_INPUT_BYTES");
+
+ err_code =
+ ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_EXECUTE, IA_CMD_TYPE_DO_EXECUTE, nullptr);
+ RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_DO_EXECUTE");
+
+ memcpy(mOutputBuffer, mDrcOutBuf, *outBytes);
+ }
+ num_preroll--;
+ } while (num_preroll > 0);
}
return IA_NO_ERROR;
}
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/core/include/C2Config.h b/media/codec2/core/include/C2Config.h
index 3820f90..f9eb2fa 100644
--- a/media/codec2/core/include/C2Config.h
+++ b/media/codec2/core/include/C2Config.h
@@ -243,6 +243,9 @@
kParamIndexTimestampGapAdjustment, // input-surface, struct
kParamIndexSurfaceAllocator, // u32
+
+ // low latency mode for decoders
+ kParamIndexLowLatencyMode, // bool
};
}
@@ -521,6 +524,7 @@
PROFILE_DV_HE_07 = _C2_PL_DV_BASE + 7, ///< Dolby Vision dvhe.07 profile
PROFILE_DV_HE_08 = _C2_PL_DV_BASE + 8, ///< Dolby Vision dvhe.08 profile
PROFILE_DV_AV_09 = _C2_PL_DV_BASE + 9, ///< Dolby Vision dvav.09 profile
+ PROFILE_DV_AV1_10 = _C2_PL_DV_BASE + 10, ///< Dolby Vision dav1.10 profile
// AV1 profiles
PROFILE_AV1_0 = _C2_PL_AV1_BASE, ///< AV1 Profile 0 (4:2:0, 8 to 10 bit)
@@ -804,6 +808,15 @@
constexpr char C2_PARAMKEY_PIPELINE_DELAY[] = "algo.delay";
/**
+ * Enable/disable low latency decoding mode.
+ * If true, low latency decoding mode is enabled, and the decoder doesn't hold input and output
+ * data more than required by the codec standards.
+ */
+typedef C2GlobalParam<C2Tuning, C2EasyBoolValue, kParamIndexLowLatencyMode>
+ C2GlobalLowLatencyModeTuning;
+constexpr char C2_PARAMKEY_LOW_LATENCY_MODE[] = "algo.low-latency";
+
+/**
* Reference characteristics.
*
* The component may hold onto input and output buffers even after completing the corresponding
diff --git a/media/codec2/hidl/1.0/utils/Android.bp b/media/codec2/hidl/1.0/utils/Android.bp
index bdff29a..b96d29b 100644
--- a/media/codec2/hidl/1.0/utils/Android.bp
+++ b/media/codec2/hidl/1.0/utils/Android.bp
@@ -6,7 +6,7 @@
defaults: ["hidl_defaults"],
srcs: [
- "ClientBlockHelper.cpp",
+ "OutputBufferQueue.cpp",
"types.cpp",
],
@@ -63,6 +63,7 @@
],
header_libs: [
+ "libbinder_headers",
"libsystem_headers",
"libcodec2_internal", // private
],
@@ -103,7 +104,7 @@
// public dependency for Codec 2.0 HAL service implementations
cc_defaults {
- name: "libcodec2-hidl-defaults",
+ name: "libcodec2-hidl-defaults@1.0",
defaults: ["libcodec2-impl-defaults"],
shared_libs: [
@@ -114,7 +115,7 @@
// public dependency for Codec 2.0 HAL client
cc_defaults {
- name: "libcodec2-hidl-client-defaults",
+ name: "libcodec2-hidl-client-defaults@1.0",
defaults: ["libcodec2-impl-defaults"],
shared_libs: [
diff --git a/media/codec2/hidl/1.0/utils/ClientBlockHelper.cpp b/media/codec2/hidl/1.0/utils/ClientBlockHelper.cpp
deleted file mode 100644
index 50790bc..0000000
--- a/media/codec2/hidl/1.0/utils/ClientBlockHelper.cpp
+++ /dev/null
@@ -1,371 +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 "Codec2-block_helper"
-#include <android-base/logging.h>
-
-#include <android/hardware/graphics/bufferqueue/2.0/IGraphicBufferProducer.h>
-#include <codec2/hidl/1.0/ClientBlockHelper.h>
-#include <gui/bufferqueue/2.0/B2HGraphicBufferProducer.h>
-
-#include <C2AllocatorGralloc.h>
-#include <C2BlockInternal.h>
-#include <C2Buffer.h>
-#include <C2PlatformSupport.h>
-
-#include <iomanip>
-
-namespace android {
-namespace hardware {
-namespace media {
-namespace c2 {
-namespace V1_0 {
-namespace utils {
-
-using HGraphicBufferProducer = ::android::hardware::graphics::bufferqueue::
- V2_0::IGraphicBufferProducer;
-using B2HGraphicBufferProducer = ::android::hardware::graphics::bufferqueue::
- V2_0::utils::B2HGraphicBufferProducer;
-
-namespace /* unnamed */ {
-
-// Create a GraphicBuffer object from a graphic block.
-sp<GraphicBuffer> createGraphicBuffer(const C2ConstGraphicBlock& block) {
- uint32_t width;
- uint32_t height;
- uint32_t format;
- uint64_t usage;
- uint32_t stride;
- uint32_t generation;
- uint64_t bqId;
- int32_t bqSlot;
- _UnwrapNativeCodec2GrallocMetadata(
- block.handle(), &width, &height, &format, &usage,
- &stride, &generation, &bqId, reinterpret_cast<uint32_t*>(&bqSlot));
- native_handle_t *grallocHandle =
- UnwrapNativeCodec2GrallocHandle(block.handle());
- sp<GraphicBuffer> graphicBuffer =
- new GraphicBuffer(grallocHandle,
- GraphicBuffer::CLONE_HANDLE,
- width, height, format,
- 1, usage, stride);
- native_handle_delete(grallocHandle);
- return graphicBuffer;
-}
-
-template <typename BlockProcessor>
-void forEachBlock(C2FrameData& frameData,
- BlockProcessor process) {
- for (const std::shared_ptr<C2Buffer>& buffer : frameData.buffers) {
- if (buffer) {
- for (const C2ConstGraphicBlock& block :
- buffer->data().graphicBlocks()) {
- process(block);
- }
- }
- }
-}
-
-template <typename BlockProcessor>
-void forEachBlock(const std::list<std::unique_ptr<C2Work>>& workList,
- BlockProcessor process) {
- for (const std::unique_ptr<C2Work>& work : workList) {
- if (!work) {
- continue;
- }
- for (const std::unique_ptr<C2Worklet>& worklet : work->worklets) {
- if (worklet) {
- forEachBlock(worklet->output, process);
- }
- }
- }
-}
-
-sp<HGraphicBufferProducer> getHgbp(const sp<IGraphicBufferProducer>& igbp) {
- sp<HGraphicBufferProducer> hgbp =
- igbp->getHalInterface<HGraphicBufferProducer>();
- return hgbp ? hgbp :
- new B2HGraphicBufferProducer(igbp);
-}
-
-status_t attachToBufferQueue(const C2ConstGraphicBlock& block,
- const sp<IGraphicBufferProducer>& igbp,
- uint32_t generation,
- int32_t* bqSlot) {
- if (!igbp) {
- LOG(WARNING) << "attachToBufferQueue -- null producer.";
- return NO_INIT;
- }
-
- sp<GraphicBuffer> graphicBuffer = createGraphicBuffer(block);
- graphicBuffer->setGenerationNumber(generation);
-
- LOG(VERBOSE) << "attachToBufferQueue -- attaching buffer:"
- << " block dimension " << block.width() << "x"
- << block.height()
- << ", graphicBuffer dimension " << graphicBuffer->getWidth() << "x"
- << graphicBuffer->getHeight()
- << std::hex << std::setfill('0')
- << ", format 0x" << std::setw(8) << graphicBuffer->getPixelFormat()
- << ", usage 0x" << std::setw(16) << graphicBuffer->getUsage()
- << std::dec << std::setfill(' ')
- << ", stride " << graphicBuffer->getStride()
- << ", generation " << graphicBuffer->getGenerationNumber();
-
- status_t result = igbp->attachBuffer(bqSlot, graphicBuffer);
- if (result != OK) {
- LOG(WARNING) << "attachToBufferQueue -- attachBuffer failed: "
- "status = " << result << ".";
- return result;
- }
- LOG(VERBOSE) << "attachToBufferQueue -- attachBuffer returned slot #"
- << *bqSlot << ".";
- return OK;
-}
-
-bool getBufferQueueAssignment(const C2ConstGraphicBlock& block,
- uint32_t* generation,
- uint64_t* bqId,
- int32_t* bqSlot) {
- return _C2BlockFactory::GetBufferQueueData(
- _C2BlockFactory::GetGraphicBlockPoolData(block),
- generation, bqId, bqSlot);
-}
-} // unnamed namespace
-
-class OutputBufferQueue::Impl {
- std::mutex mMutex;
- sp<IGraphicBufferProducer> mIgbp;
- uint32_t mGeneration;
- uint64_t mBqId;
- std::shared_ptr<int> mOwner;
- // To migrate existing buffers
- sp<GraphicBuffer> mBuffers[BufferQueueDefs::NUM_BUFFER_SLOTS]; // find a better way
- std::weak_ptr<_C2BlockPoolData>
- mPoolDatas[BufferQueueDefs::NUM_BUFFER_SLOTS];
-
-public:
- Impl(): mGeneration(0), mBqId(0) {}
-
- bool configure(const sp<IGraphicBufferProducer>& igbp,
- uint32_t generation,
- uint64_t bqId) {
- size_t tryNum = 0;
- size_t success = 0;
- sp<GraphicBuffer> buffers[BufferQueueDefs::NUM_BUFFER_SLOTS];
- std::weak_ptr<_C2BlockPoolData>
- poolDatas[BufferQueueDefs::NUM_BUFFER_SLOTS];
- {
- std::scoped_lock<std::mutex> l(mMutex);
- if (generation == mGeneration) {
- return false;
- }
- mIgbp = igbp;
- mGeneration = generation;
- mBqId = bqId;
- mOwner = std::make_shared<int>(0);
- for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; ++i) {
- if (mBqId == 0 || !mBuffers[i]) {
- continue;
- }
- std::shared_ptr<_C2BlockPoolData> data = mPoolDatas[i].lock();
- if (!data ||
- !_C2BlockFactory::BeginAttachBlockToBufferQueue(data)) {
- continue;
- }
- ++tryNum;
- int bqSlot;
- mBuffers[i]->setGenerationNumber(generation);
- status_t result = igbp->attachBuffer(&bqSlot, mBuffers[i]);
- if (result != OK) {
- continue;
- }
- bool attach =
- _C2BlockFactory::EndAttachBlockToBufferQueue(
- data, mOwner, getHgbp(mIgbp),
- generation, bqId, bqSlot);
- if (!attach) {
- igbp->cancelBuffer(bqSlot, Fence::NO_FENCE);
- continue;
- }
- buffers[bqSlot] = mBuffers[i];
- poolDatas[bqSlot] = data;
- ++success;
- }
- for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; ++i) {
- mBuffers[i] = buffers[i];
- mPoolDatas[i] = poolDatas[i];
- }
- }
- ALOGD("remote graphic buffer migration %zu/%zu", success, tryNum);
- return true;
- }
-
- bool registerBuffer(const C2ConstGraphicBlock& block) {
- std::shared_ptr<_C2BlockPoolData> data =
- _C2BlockFactory::GetGraphicBlockPoolData(block);
- if (!data) {
- return false;
- }
- std::scoped_lock<std::mutex> l(mMutex);
-
- if (!mIgbp) {
- return false;
- }
-
- uint32_t oldGeneration;
- uint64_t oldId;
- int32_t oldSlot;
- // If the block is not bufferqueue-based, do nothing.
- if (!_C2BlockFactory::GetBufferQueueData(
- data, &oldGeneration, &oldId, &oldSlot) || (oldId == 0)) {
- return false;
- }
- // If the block's bqId is the same as the desired bqId, just hold.
- if ((oldId == mBqId) && (oldGeneration == mGeneration)) {
- LOG(VERBOSE) << "holdBufferQueueBlock -- import without attaching:"
- << " bqId " << oldId
- << ", bqSlot " << oldSlot
- << ", generation " << mGeneration
- << ".";
- _C2BlockFactory::HoldBlockFromBufferQueue(data, mOwner, getHgbp(mIgbp));
- mPoolDatas[oldSlot] = data;
- mBuffers[oldSlot] = createGraphicBuffer(block);
- mBuffers[oldSlot]->setGenerationNumber(mGeneration);
- return true;
- }
- int32_t d = (int32_t) mGeneration - (int32_t) oldGeneration;
- LOG(WARNING) << "receiving stale buffer: generation "
- << mGeneration << " , diff " << d << " : slot "
- << oldSlot;
- return false;
- }
-
- status_t outputBuffer(
- const C2ConstGraphicBlock& block,
- const BnGraphicBufferProducer::QueueBufferInput& input,
- BnGraphicBufferProducer::QueueBufferOutput* output) {
- uint32_t generation;
- uint64_t bqId;
- int32_t bqSlot;
- bool display = displayBufferQueueBlock(block);
- if (!getBufferQueueAssignment(block, &generation, &bqId, &bqSlot) ||
- bqId == 0) {
- // Block not from bufferqueue -- it must be attached before queuing.
-
- mMutex.lock();
- sp<IGraphicBufferProducer> outputIgbp = mIgbp;
- uint32_t outputGeneration = mGeneration;
- mMutex.unlock();
-
- status_t status = attachToBufferQueue(
- block, outputIgbp, outputGeneration, &bqSlot);
- if (status != OK) {
- LOG(WARNING) << "outputBuffer -- attaching failed.";
- return INVALID_OPERATION;
- }
-
- status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
- input, output);
- if (status != OK) {
- LOG(ERROR) << "outputBuffer -- queueBuffer() failed "
- "on non-bufferqueue-based block. "
- "Error = " << status << ".";
- return status;
- }
- return OK;
- }
-
- mMutex.lock();
- sp<IGraphicBufferProducer> outputIgbp = mIgbp;
- uint32_t outputGeneration = mGeneration;
- uint64_t outputBqId = mBqId;
- mMutex.unlock();
-
- if (!outputIgbp) {
- LOG(VERBOSE) << "outputBuffer -- output surface is null.";
- return NO_INIT;
- }
-
- if (!display) {
- LOG(WARNING) << "outputBuffer -- cannot display "
- "bufferqueue-based block to the bufferqueue.";
- return UNKNOWN_ERROR;
- }
- if (bqId != outputBqId || generation != outputGeneration) {
- int32_t diff = (int32_t) outputGeneration - (int32_t) generation;
- LOG(WARNING) << "outputBuffer -- buffers from old generation to "
- << outputGeneration << " , diff: " << diff
- << " , slot: " << bqSlot;
- return DEAD_OBJECT;
- }
-
- status_t status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
- input, output);
- if (status != OK) {
- LOG(ERROR) << "outputBuffer -- queueBuffer() failed "
- "on bufferqueue-based block. "
- "Error = " << status << ".";
- return status;
- }
- return OK;
- }
-
- Impl *getPtr() {
- return this;
- }
-
- ~Impl() {}
-};
-
-OutputBufferQueue::OutputBufferQueue(): mImpl(new Impl()) {}
-
-OutputBufferQueue::~OutputBufferQueue() {}
-
-bool OutputBufferQueue::configure(const sp<IGraphicBufferProducer>& igbp,
- uint32_t generation,
- uint64_t bqId) {
- return mImpl && mImpl->configure(igbp, generation, bqId);
-}
-
-status_t OutputBufferQueue::outputBuffer(
- const C2ConstGraphicBlock& block,
- const BnGraphicBufferProducer::QueueBufferInput& input,
- BnGraphicBufferProducer::QueueBufferOutput* output) {
- if (mImpl) {
- return mImpl->outputBuffer(block, input, output);
- }
- return DEAD_OBJECT;
-}
-
-void OutputBufferQueue::holdBufferQueueBlocks(
- const std::list<std::unique_ptr<C2Work>>& workList) {
- if (!mImpl) {
- return;
- }
- forEachBlock(workList,
- std::bind(&OutputBufferQueue::Impl::registerBuffer,
- mImpl->getPtr(), std::placeholders::_1));
-}
-
-} // namespace utils
-} // namespace V1_0
-} // namespace c2
-} // namespace media
-} // namespace hardware
-} // namespace android
-
diff --git a/media/codec2/hidl/1.0/utils/Component.cpp b/media/codec2/hidl/1.0/utils/Component.cpp
index a9f20a4..8a84601 100644
--- a/media/codec2/hidl/1.0/utils/Component.cpp
+++ b/media/codec2/hidl/1.0/utils/Component.cpp
@@ -204,7 +204,8 @@
const sp<::android::hardware::media::bufferpool::V2_0::
IClientManager>& clientPoolManager)
: mComponent{component},
- mInterface{new ComponentInterface(component->intf(), store.get())},
+ mInterface{new ComponentInterface(component->intf(),
+ store->getParameterCache())},
mListener{listener},
mStore{store},
mBufferPoolSender{clientPoolManager} {
diff --git a/media/codec2/hidl/1.0/utils/ComponentInterface.cpp b/media/codec2/hidl/1.0/utils/ComponentInterface.cpp
index 39e5357..12078e0 100644
--- a/media/codec2/hidl/1.0/utils/ComponentInterface.cpp
+++ b/media/codec2/hidl/1.0/utils/ComponentInterface.cpp
@@ -87,10 +87,10 @@
// ComponentInterface
ComponentInterface::ComponentInterface(
const std::shared_ptr<C2ComponentInterface>& intf,
- ComponentStore* store)
+ const std::shared_ptr<ParameterCache>& cache)
: mInterface{intf},
mConfigurable{new CachedConfigurable(std::make_unique<CompIntf>(intf))} {
- mInit = mConfigurable->init(store);
+ mInit = mConfigurable->init(cache);
}
c2_status_t ComponentInterface::status() const {
diff --git a/media/codec2/hidl/1.0/utils/ComponentStore.cpp b/media/codec2/hidl/1.0/utils/ComponentStore.cpp
index 1e0a190..9b9d449 100644
--- a/media/codec2/hidl/1.0/utils/ComponentStore.cpp
+++ b/media/codec2/hidl/1.0/utils/ComponentStore.cpp
@@ -102,8 +102,29 @@
} // unnamed namespace
+struct ComponentStore::StoreParameterCache : public ParameterCache {
+ std::mutex mStoreMutex;
+ ComponentStore* mStore;
+
+ StoreParameterCache(ComponentStore* store): mStore{store} {
+ }
+
+ virtual c2_status_t validate(
+ const std::vector<std::shared_ptr<C2ParamDescriptor>>& params
+ ) override {
+ std::scoped_lock _lock(mStoreMutex);
+ return mStore ? mStore->validateSupportedParams(params) : C2_NO_INIT;
+ }
+
+ void onStoreDestroyed() {
+ std::scoped_lock _lock(mStoreMutex);
+ mStore = nullptr;
+ }
+};
+
ComponentStore::ComponentStore(const std::shared_ptr<C2ComponentStore>& store)
: mConfigurable{new CachedConfigurable(std::make_unique<StoreIntf>(store))},
+ mParameterCache{std::make_shared<StoreParameterCache>(this)},
mStore{store} {
std::shared_ptr<C2ComponentStore> platformStore = android::GetCodec2PlatformComponentStore();
@@ -113,7 +134,12 @@
mParamReflector = mStore->getParamReflector();
// Retrieve supported parameters from store
- mInit = mConfigurable->init(this);
+ using namespace std::placeholders;
+ mInit = mConfigurable->init(mParameterCache);
+}
+
+ComponentStore::~ComponentStore() {
+ mParameterCache->onStoreDestroyed();
}
c2_status_t ComponentStore::status() const {
@@ -146,6 +172,10 @@
return res;
}
+std::shared_ptr<ParameterCache> ComponentStore::getParameterCache() const {
+ return mParameterCache;
+}
+
// Methods from ::android::hardware::media::c2::V1_0::IComponentStore
Return<void> ComponentStore::createComponent(
const hidl_string& name,
@@ -187,7 +217,7 @@
sp<IComponentInterface> interface;
if (res == C2_OK) {
onInterfaceLoaded(c2interface);
- interface = new ComponentInterface(c2interface, this);
+ interface = new ComponentInterface(c2interface, mParameterCache);
}
_hidl_cb(static_cast<Status>(res), interface);
return Void();
@@ -218,8 +248,9 @@
_hidl_cb(Status::CORRUPTED, nullptr);
return Void();
}
+ using namespace std::placeholders;
sp<InputSurface> inputSurface = new InputSurface(
- this,
+ mParameterCache,
std::make_shared<C2ReflectorHelper>(),
source->getHGraphicBufferProducer(),
source);
diff --git a/media/codec2/hidl/1.0/utils/Configurable.cpp b/media/codec2/hidl/1.0/utils/Configurable.cpp
index ec9c170..530576d 100644
--- a/media/codec2/hidl/1.0/utils/Configurable.cpp
+++ b/media/codec2/hidl/1.0/utils/Configurable.cpp
@@ -38,10 +38,11 @@
: mIntf{std::move(intf)} {
}
-c2_status_t CachedConfigurable::init(ComponentStore* store) {
+c2_status_t CachedConfigurable::init(
+ const std::shared_ptr<ParameterCache>& cache) {
// Retrieve supported parameters from store
c2_status_t init = mIntf->querySupportedParams(&mSupportedParams);
- c2_status_t validate = store->validateSupportedParams(mSupportedParams);
+ c2_status_t validate = cache->validate(mSupportedParams);
return init == C2_OK ? C2_OK : validate;
}
diff --git a/media/codec2/hidl/1.0/utils/InputBufferManager.cpp b/media/codec2/hidl/1.0/utils/InputBufferManager.cpp
index a023a05..8c0d0a4 100644
--- a/media/codec2/hidl/1.0/utils/InputBufferManager.cpp
+++ b/media/codec2/hidl/1.0/utils/InputBufferManager.cpp
@@ -70,7 +70,7 @@
<< ".";
std::lock_guard<std::mutex> lock(mMutex);
- std::set<TrackedBuffer> &bufferIds =
+ std::set<TrackedBuffer*> &bufferIds =
mTrackedBuffersMap[listener][frameIndex];
for (size_t i = 0; i < input.buffers.size(); ++i) {
@@ -79,13 +79,14 @@
<< "Input buffer at index " << i << " is null.";
continue;
}
- const TrackedBuffer &bufferId =
- *bufferIds.emplace(listener, frameIndex, i, input.buffers[i]).
- first;
+ TrackedBuffer *bufferId =
+ new TrackedBuffer(listener, frameIndex, i, input.buffers[i]);
+ mTrackedBufferCache.emplace(bufferId);
+ bufferIds.emplace(bufferId);
c2_status_t status = input.buffers[i]->registerOnDestroyNotify(
onBufferDestroyed,
- const_cast<void*>(reinterpret_cast<const void*>(&bufferId)));
+ reinterpret_cast<void*>(bufferId));
if (status != C2_OK) {
LOG(DEBUG) << "InputBufferManager::_registerFrameData -- "
<< "registerOnDestroyNotify() failed "
@@ -119,31 +120,32 @@
auto findListener = mTrackedBuffersMap.find(listener);
if (findListener != mTrackedBuffersMap.end()) {
- std::map<uint64_t, std::set<TrackedBuffer>> &frameIndex2BufferIds
+ std::map<uint64_t, std::set<TrackedBuffer*>> &frameIndex2BufferIds
= findListener->second;
auto findFrameIndex = frameIndex2BufferIds.find(frameIndex);
if (findFrameIndex != frameIndex2BufferIds.end()) {
- std::set<TrackedBuffer> &bufferIds = findFrameIndex->second;
- for (const TrackedBuffer& bufferId : bufferIds) {
- std::shared_ptr<C2Buffer> buffer = bufferId.buffer.lock();
+ std::set<TrackedBuffer*> &bufferIds = findFrameIndex->second;
+ for (TrackedBuffer* bufferId : bufferIds) {
+ std::shared_ptr<C2Buffer> buffer = bufferId->buffer.lock();
if (buffer) {
c2_status_t status = buffer->unregisterOnDestroyNotify(
onBufferDestroyed,
- const_cast<void*>(
- reinterpret_cast<const void*>(&bufferId)));
+ reinterpret_cast<void*>(bufferId));
if (status != C2_OK) {
LOG(DEBUG) << "InputBufferManager::_unregisterFrameData "
<< "-- unregisterOnDestroyNotify() failed "
<< "(listener @ 0x"
<< std::hex
- << bufferId.listener.unsafe_get()
+ << bufferId->listener.unsafe_get()
<< ", frameIndex = "
- << std::dec << bufferId.frameIndex
- << ", bufferIndex = " << bufferId.bufferIndex
+ << std::dec << bufferId->frameIndex
+ << ", bufferIndex = " << bufferId->bufferIndex
<< ") => status = " << status
<< ".";
}
}
+ mTrackedBufferCache.erase(bufferId);
+ delete bufferId;
}
frameIndex2BufferIds.erase(findFrameIndex);
@@ -179,31 +181,32 @@
auto findListener = mTrackedBuffersMap.find(listener);
if (findListener != mTrackedBuffersMap.end()) {
- std::map<uint64_t, std::set<TrackedBuffer>> &frameIndex2BufferIds =
+ std::map<uint64_t, std::set<TrackedBuffer*>> &frameIndex2BufferIds =
findListener->second;
for (auto findFrameIndex = frameIndex2BufferIds.begin();
findFrameIndex != frameIndex2BufferIds.end();
++findFrameIndex) {
- std::set<TrackedBuffer> &bufferIds = findFrameIndex->second;
- for (const TrackedBuffer& bufferId : bufferIds) {
- std::shared_ptr<C2Buffer> buffer = bufferId.buffer.lock();
+ std::set<TrackedBuffer*> &bufferIds = findFrameIndex->second;
+ for (TrackedBuffer* bufferId : bufferIds) {
+ std::shared_ptr<C2Buffer> buffer = bufferId->buffer.lock();
if (buffer) {
c2_status_t status = buffer->unregisterOnDestroyNotify(
onBufferDestroyed,
- const_cast<void*>(
- reinterpret_cast<const void*>(&bufferId)));
+ reinterpret_cast<void*>(bufferId));
if (status != C2_OK) {
LOG(DEBUG) << "InputBufferManager::_unregisterFrameData "
<< "-- unregisterOnDestroyNotify() failed "
<< "(listener @ 0x"
<< std::hex
- << bufferId.listener.unsafe_get()
+ << bufferId->listener.unsafe_get()
<< ", frameIndex = "
- << std::dec << bufferId.frameIndex
- << ", bufferIndex = " << bufferId.bufferIndex
+ << std::dec << bufferId->frameIndex
+ << ", bufferIndex = " << bufferId->bufferIndex
<< ") => status = " << status
<< ".";
}
+ mTrackedBufferCache.erase(bufferId);
+ delete bufferId;
}
}
}
@@ -236,50 +239,59 @@
<< std::dec << ".";
return;
}
- TrackedBuffer id(*reinterpret_cast<TrackedBuffer*>(arg));
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ TrackedBuffer *bufferId = reinterpret_cast<TrackedBuffer*>(arg);
+
+ if (mTrackedBufferCache.find(bufferId) == mTrackedBufferCache.end()) {
+ LOG(VERBOSE) << "InputBufferManager::_onBufferDestroyed -- called with "
+ << "unregistered buffer: "
+ << "buf @ 0x" << std::hex << buf
+ << ", arg @ 0x" << std::hex << arg
+ << std::dec << ".";
+ return;
+ }
+
LOG(VERBOSE) << "InputBufferManager::_onBufferDestroyed -- called with "
<< "buf @ 0x" << std::hex << buf
<< ", arg @ 0x" << std::hex << arg
<< std::dec << " -- "
- << "listener @ 0x" << std::hex << id.listener.unsafe_get()
- << ", frameIndex = " << std::dec << id.frameIndex
- << ", bufferIndex = " << id.bufferIndex
+ << "listener @ 0x" << std::hex << bufferId->listener.unsafe_get()
+ << ", frameIndex = " << std::dec << bufferId->frameIndex
+ << ", bufferIndex = " << bufferId->bufferIndex
<< ".";
-
- std::lock_guard<std::mutex> lock(mMutex);
-
- auto findListener = mTrackedBuffersMap.find(id.listener);
+ auto findListener = mTrackedBuffersMap.find(bufferId->listener);
if (findListener == mTrackedBuffersMap.end()) {
- LOG(DEBUG) << "InputBufferManager::_onBufferDestroyed -- "
- << "received invalid listener: "
- << "listener @ 0x" << std::hex << id.listener.unsafe_get()
- << " (frameIndex = " << std::dec << id.frameIndex
- << ", bufferIndex = " << id.bufferIndex
- << ").";
+ LOG(VERBOSE) << "InputBufferManager::_onBufferDestroyed -- "
+ << "received invalid listener: "
+ << "listener @ 0x" << std::hex << bufferId->listener.unsafe_get()
+ << " (frameIndex = " << std::dec << bufferId->frameIndex
+ << ", bufferIndex = " << bufferId->bufferIndex
+ << ").";
return;
}
- std::map<uint64_t, std::set<TrackedBuffer>> &frameIndex2BufferIds
+ std::map<uint64_t, std::set<TrackedBuffer*>> &frameIndex2BufferIds
= findListener->second;
- auto findFrameIndex = frameIndex2BufferIds.find(id.frameIndex);
+ auto findFrameIndex = frameIndex2BufferIds.find(bufferId->frameIndex);
if (findFrameIndex == frameIndex2BufferIds.end()) {
LOG(DEBUG) << "InputBufferManager::_onBufferDestroyed -- "
<< "received invalid frame index: "
- << "frameIndex = " << id.frameIndex
- << " (listener @ 0x" << std::hex << id.listener.unsafe_get()
- << ", bufferIndex = " << std::dec << id.bufferIndex
+ << "frameIndex = " << bufferId->frameIndex
+ << " (listener @ 0x" << std::hex << bufferId->listener.unsafe_get()
+ << ", bufferIndex = " << std::dec << bufferId->bufferIndex
<< ").";
return;
}
- std::set<TrackedBuffer> &bufferIds = findFrameIndex->second;
- auto findBufferId = bufferIds.find(id);
+ std::set<TrackedBuffer*> &bufferIds = findFrameIndex->second;
+ auto findBufferId = bufferIds.find(bufferId);
if (findBufferId == bufferIds.end()) {
LOG(DEBUG) << "InputBufferManager::_onBufferDestroyed -- "
<< "received invalid buffer index: "
- << "bufferIndex = " << id.bufferIndex
- << " (frameIndex = " << id.frameIndex
- << ", listener @ 0x" << std::hex << id.listener.unsafe_get()
+ << "bufferIndex = " << bufferId->bufferIndex
+ << " (frameIndex = " << bufferId->frameIndex
+ << ", listener @ 0x" << std::hex << bufferId->listener.unsafe_get()
<< std::dec << ").";
return;
}
@@ -292,10 +304,13 @@
}
}
- DeathNotifications &deathNotifications = mDeathNotifications[id.listener];
- deathNotifications.indices[id.frameIndex].emplace_back(id.bufferIndex);
+ DeathNotifications &deathNotifications = mDeathNotifications[bufferId->listener];
+ deathNotifications.indices[bufferId->frameIndex].emplace_back(bufferId->bufferIndex);
++deathNotifications.count;
mOnBufferDestroyed.notify_one();
+
+ mTrackedBufferCache.erase(bufferId);
+ delete bufferId;
}
// Notify the clients about buffer destructions.
diff --git a/media/codec2/hidl/1.0/utils/InputSurface.cpp b/media/codec2/hidl/1.0/utils/InputSurface.cpp
index 2b4ca85..c3c32e9 100644
--- a/media/codec2/hidl/1.0/utils/InputSurface.cpp
+++ b/media/codec2/hidl/1.0/utils/InputSurface.cpp
@@ -137,9 +137,9 @@
}
std::shared_ptr<C2Component> comp = Component::findLocalComponent(sink);
if (comp) {
- connection = new InputSurfaceConnection(mSource, comp, mStore);
+ connection = new InputSurfaceConnection(mSource, comp, mParameterCache);
} else {
- connection = new InputSurfaceConnection(mSource, sink, mStore);
+ connection = new InputSurfaceConnection(mSource, sink, mParameterCache);
}
if (!connection->init()) {
connection = nullptr;
@@ -153,11 +153,11 @@
// Constructor is exclusive to ComponentStore.
InputSurface::InputSurface(
- const sp<ComponentStore>& store,
+ const std::shared_ptr<ParameterCache>& cache,
const std::shared_ptr<C2ReflectorHelper>& reflector,
const sp<HGraphicBufferProducer>& producer,
const sp<GraphicBufferSource>& source)
- : mStore{store},
+ : mParameterCache{cache},
mProducer{producer},
mSource{source},
mIntf{std::make_shared<Interface>(reflector)},
@@ -165,7 +165,7 @@
std::make_unique<ConfigurableIntf>(
mIntf, source))} {
- mConfigurable->init(store.get());
+ mConfigurable->init(mParameterCache);
}
} // namespace utils
diff --git a/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp b/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp
index c9932ef..5cc0d53 100644
--- a/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp
+++ b/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp
@@ -445,21 +445,21 @@
InputSurfaceConnection::InputSurfaceConnection(
const sp<GraphicBufferSource>& source,
const std::shared_ptr<C2Component>& comp,
- const sp<ComponentStore>& store)
+ const std::shared_ptr<ParameterCache>& cache)
: mImpl{new Impl(source, comp)},
mConfigurable{new CachedConfigurable(
std::make_unique<Impl::ConfigurableIntf>(mImpl))} {
- mConfigurable->init(store.get());
+ mConfigurable->init(cache);
}
InputSurfaceConnection::InputSurfaceConnection(
const sp<GraphicBufferSource>& source,
const sp<IInputSink>& sink,
- const sp<ComponentStore>& store)
+ const std::shared_ptr<ParameterCache>& cache)
: mImpl{new Impl(source, sink)},
mConfigurable{new CachedConfigurable(
std::make_unique<Impl::ConfigurableIntf>(mImpl))} {
- mConfigurable->init(store.get());
+ mConfigurable->init(cache);
}
Return<Status> InputSurfaceConnection::disconnect() {
diff --git a/media/codec2/hidl/1.0/utils/OutputBufferQueue.cpp b/media/codec2/hidl/1.0/utils/OutputBufferQueue.cpp
new file mode 100644
index 0000000..c4a72ef
--- /dev/null
+++ b/media/codec2/hidl/1.0/utils/OutputBufferQueue.cpp
@@ -0,0 +1,335 @@
+/*
+ * 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 "Codec2-OutputBufferQueue"
+#include <android-base/logging.h>
+
+#include <android/hardware/graphics/bufferqueue/2.0/IGraphicBufferProducer.h>
+#include <codec2/hidl/1.0/OutputBufferQueue.h>
+#include <gui/bufferqueue/2.0/B2HGraphicBufferProducer.h>
+
+#include <C2AllocatorGralloc.h>
+#include <C2BlockInternal.h>
+#include <C2Buffer.h>
+#include <C2PlatformSupport.h>
+
+#include <iomanip>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace utils {
+
+using HGraphicBufferProducer = ::android::hardware::graphics::bufferqueue::
+ V2_0::IGraphicBufferProducer;
+using B2HGraphicBufferProducer = ::android::hardware::graphics::bufferqueue::
+ V2_0::utils::B2HGraphicBufferProducer;
+
+namespace /* unnamed */ {
+
+// Create a GraphicBuffer object from a graphic block.
+sp<GraphicBuffer> createGraphicBuffer(const C2ConstGraphicBlock& block) {
+ uint32_t width;
+ uint32_t height;
+ uint32_t format;
+ uint64_t usage;
+ uint32_t stride;
+ uint32_t generation;
+ uint64_t bqId;
+ int32_t bqSlot;
+ _UnwrapNativeCodec2GrallocMetadata(
+ block.handle(), &width, &height, &format, &usage,
+ &stride, &generation, &bqId, reinterpret_cast<uint32_t*>(&bqSlot));
+ native_handle_t *grallocHandle =
+ UnwrapNativeCodec2GrallocHandle(block.handle());
+ sp<GraphicBuffer> graphicBuffer =
+ new GraphicBuffer(grallocHandle,
+ GraphicBuffer::CLONE_HANDLE,
+ width, height, format,
+ 1, usage, stride);
+ native_handle_delete(grallocHandle);
+ return graphicBuffer;
+}
+
+template <typename BlockProcessor>
+void forEachBlock(C2FrameData& frameData,
+ BlockProcessor process) {
+ for (const std::shared_ptr<C2Buffer>& buffer : frameData.buffers) {
+ if (buffer) {
+ for (const C2ConstGraphicBlock& block :
+ buffer->data().graphicBlocks()) {
+ process(block);
+ }
+ }
+ }
+}
+
+template <typename BlockProcessor>
+void forEachBlock(const std::list<std::unique_ptr<C2Work>>& workList,
+ BlockProcessor process) {
+ for (const std::unique_ptr<C2Work>& work : workList) {
+ if (!work) {
+ continue;
+ }
+ for (const std::unique_ptr<C2Worklet>& worklet : work->worklets) {
+ if (worklet) {
+ forEachBlock(worklet->output, process);
+ }
+ }
+ }
+}
+
+sp<HGraphicBufferProducer> getHgbp(const sp<IGraphicBufferProducer>& igbp) {
+ sp<HGraphicBufferProducer> hgbp =
+ igbp->getHalInterface<HGraphicBufferProducer>();
+ return hgbp ? hgbp :
+ new B2HGraphicBufferProducer(igbp);
+}
+
+status_t attachToBufferQueue(const C2ConstGraphicBlock& block,
+ const sp<IGraphicBufferProducer>& igbp,
+ uint32_t generation,
+ int32_t* bqSlot) {
+ if (!igbp) {
+ LOG(WARNING) << "attachToBufferQueue -- null producer.";
+ return NO_INIT;
+ }
+
+ sp<GraphicBuffer> graphicBuffer = createGraphicBuffer(block);
+ graphicBuffer->setGenerationNumber(generation);
+
+ LOG(VERBOSE) << "attachToBufferQueue -- attaching buffer:"
+ << " block dimension " << block.width() << "x"
+ << block.height()
+ << ", graphicBuffer dimension " << graphicBuffer->getWidth() << "x"
+ << graphicBuffer->getHeight()
+ << std::hex << std::setfill('0')
+ << ", format 0x" << std::setw(8) << graphicBuffer->getPixelFormat()
+ << ", usage 0x" << std::setw(16) << graphicBuffer->getUsage()
+ << std::dec << std::setfill(' ')
+ << ", stride " << graphicBuffer->getStride()
+ << ", generation " << graphicBuffer->getGenerationNumber();
+
+ status_t result = igbp->attachBuffer(bqSlot, graphicBuffer);
+ if (result != OK) {
+ LOG(WARNING) << "attachToBufferQueue -- attachBuffer failed: "
+ "status = " << result << ".";
+ return result;
+ }
+ LOG(VERBOSE) << "attachToBufferQueue -- attachBuffer returned slot #"
+ << *bqSlot << ".";
+ return OK;
+}
+
+bool getBufferQueueAssignment(const C2ConstGraphicBlock& block,
+ uint32_t* generation,
+ uint64_t* bqId,
+ int32_t* bqSlot) {
+ return _C2BlockFactory::GetBufferQueueData(
+ _C2BlockFactory::GetGraphicBlockPoolData(block),
+ generation, bqId, bqSlot);
+}
+
+} // unnamed namespace
+
+OutputBufferQueue::OutputBufferQueue()
+ : mGeneration{0}, mBqId{0} {
+}
+
+OutputBufferQueue::~OutputBufferQueue() {
+}
+
+bool OutputBufferQueue::configure(const sp<IGraphicBufferProducer>& igbp,
+ uint32_t generation,
+ uint64_t bqId) {
+ size_t tryNum = 0;
+ size_t success = 0;
+ sp<GraphicBuffer> buffers[BufferQueueDefs::NUM_BUFFER_SLOTS];
+ std::weak_ptr<_C2BlockPoolData>
+ poolDatas[BufferQueueDefs::NUM_BUFFER_SLOTS];
+ {
+ std::scoped_lock<std::mutex> l(mMutex);
+ if (generation == mGeneration) {
+ return false;
+ }
+ mIgbp = igbp;
+ mGeneration = generation;
+ mBqId = bqId;
+ mOwner = std::make_shared<int>(0);
+ for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; ++i) {
+ if (mBqId == 0 || !mBuffers[i]) {
+ continue;
+ }
+ std::shared_ptr<_C2BlockPoolData> data = mPoolDatas[i].lock();
+ if (!data ||
+ !_C2BlockFactory::BeginAttachBlockToBufferQueue(data)) {
+ continue;
+ }
+ ++tryNum;
+ int bqSlot;
+ mBuffers[i]->setGenerationNumber(generation);
+ status_t result = igbp->attachBuffer(&bqSlot, mBuffers[i]);
+ if (result != OK) {
+ continue;
+ }
+ bool attach =
+ _C2BlockFactory::EndAttachBlockToBufferQueue(
+ data, mOwner, getHgbp(mIgbp),
+ generation, bqId, bqSlot);
+ if (!attach) {
+ igbp->cancelBuffer(bqSlot, Fence::NO_FENCE);
+ continue;
+ }
+ buffers[bqSlot] = mBuffers[i];
+ poolDatas[bqSlot] = data;
+ ++success;
+ }
+ for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; ++i) {
+ mBuffers[i] = buffers[i];
+ mPoolDatas[i] = poolDatas[i];
+ }
+ }
+ ALOGD("remote graphic buffer migration %zu/%zu", success, tryNum);
+ return true;
+}
+
+bool OutputBufferQueue::registerBuffer(const C2ConstGraphicBlock& block) {
+ std::shared_ptr<_C2BlockPoolData> data =
+ _C2BlockFactory::GetGraphicBlockPoolData(block);
+ if (!data) {
+ return false;
+ }
+ std::scoped_lock<std::mutex> l(mMutex);
+
+ if (!mIgbp) {
+ return false;
+ }
+
+ uint32_t oldGeneration;
+ uint64_t oldId;
+ int32_t oldSlot;
+ // If the block is not bufferqueue-based, do nothing.
+ if (!_C2BlockFactory::GetBufferQueueData(
+ data, &oldGeneration, &oldId, &oldSlot) || (oldId == 0)) {
+ return false;
+ }
+ // If the block's bqId is the same as the desired bqId, just hold.
+ if ((oldId == mBqId) && (oldGeneration == mGeneration)) {
+ LOG(VERBOSE) << "holdBufferQueueBlock -- import without attaching:"
+ << " bqId " << oldId
+ << ", bqSlot " << oldSlot
+ << ", generation " << mGeneration
+ << ".";
+ _C2BlockFactory::HoldBlockFromBufferQueue(data, mOwner, getHgbp(mIgbp));
+ mPoolDatas[oldSlot] = data;
+ mBuffers[oldSlot] = createGraphicBuffer(block);
+ mBuffers[oldSlot]->setGenerationNumber(mGeneration);
+ return true;
+ }
+ int32_t d = (int32_t) mGeneration - (int32_t) oldGeneration;
+ LOG(WARNING) << "receiving stale buffer: generation "
+ << mGeneration << " , diff " << d << " : slot "
+ << oldSlot;
+ return false;
+}
+
+status_t OutputBufferQueue::outputBuffer(
+ const C2ConstGraphicBlock& block,
+ const BnGraphicBufferProducer::QueueBufferInput& input,
+ BnGraphicBufferProducer::QueueBufferOutput* output) {
+ uint32_t generation;
+ uint64_t bqId;
+ int32_t bqSlot;
+ bool display = displayBufferQueueBlock(block);
+ if (!getBufferQueueAssignment(block, &generation, &bqId, &bqSlot) ||
+ bqId == 0) {
+ // Block not from bufferqueue -- it must be attached before queuing.
+
+ mMutex.lock();
+ sp<IGraphicBufferProducer> outputIgbp = mIgbp;
+ uint32_t outputGeneration = mGeneration;
+ mMutex.unlock();
+
+ status_t status = attachToBufferQueue(
+ block, outputIgbp, outputGeneration, &bqSlot);
+ if (status != OK) {
+ LOG(WARNING) << "outputBuffer -- attaching failed.";
+ return INVALID_OPERATION;
+ }
+
+ status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
+ input, output);
+ if (status != OK) {
+ LOG(ERROR) << "outputBuffer -- queueBuffer() failed "
+ "on non-bufferqueue-based block. "
+ "Error = " << status << ".";
+ return status;
+ }
+ return OK;
+ }
+
+ mMutex.lock();
+ sp<IGraphicBufferProducer> outputIgbp = mIgbp;
+ uint32_t outputGeneration = mGeneration;
+ uint64_t outputBqId = mBqId;
+ mMutex.unlock();
+
+ if (!outputIgbp) {
+ LOG(VERBOSE) << "outputBuffer -- output surface is null.";
+ return NO_INIT;
+ }
+
+ if (!display) {
+ LOG(WARNING) << "outputBuffer -- cannot display "
+ "bufferqueue-based block to the bufferqueue.";
+ return UNKNOWN_ERROR;
+ }
+ if (bqId != outputBqId || generation != outputGeneration) {
+ int32_t diff = (int32_t) outputGeneration - (int32_t) generation;
+ LOG(WARNING) << "outputBuffer -- buffers from old generation to "
+ << outputGeneration << " , diff: " << diff
+ << " , slot: " << bqSlot;
+ return DEAD_OBJECT;
+ }
+
+ status_t status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
+ input, output);
+ if (status != OK) {
+ LOG(ERROR) << "outputBuffer -- queueBuffer() failed "
+ "on bufferqueue-based block. "
+ "Error = " << status << ".";
+ return status;
+ }
+ return OK;
+}
+
+void OutputBufferQueue::holdBufferQueueBlocks(
+ const std::list<std::unique_ptr<C2Work>>& workList) {
+ forEachBlock(workList,
+ std::bind(&OutputBufferQueue::registerBuffer,
+ this, std::placeholders::_1));
+}
+
+} // namespace utils
+} // namespace V1_0
+} // namespace c2
+} // namespace media
+} // namespace hardware
+} // namespace android
+
diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentInterface.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentInterface.h
index a5d235e..9102f92 100644
--- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentInterface.h
+++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentInterface.h
@@ -45,7 +45,7 @@
struct ComponentInterface : public IComponentInterface {
ComponentInterface(
const std::shared_ptr<C2ComponentInterface>& interface,
- ComponentStore* store);
+ const std::shared_ptr<ParameterCache>& cache);
c2_status_t status() const;
virtual Return<sp<IConfigurable>> getConfigurable() override;
diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentStore.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentStore.h
index be80c62..fe7d048 100644
--- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentStore.h
+++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentStore.h
@@ -54,7 +54,7 @@
struct ComponentStore : public IComponentStore {
ComponentStore(const std::shared_ptr<C2ComponentStore>& store);
- virtual ~ComponentStore() = default;
+ virtual ~ComponentStore();
/**
* Returns the status of the construction of this object.
@@ -68,6 +68,12 @@
c2_status_t validateSupportedParams(
const std::vector<std::shared_ptr<C2ParamDescriptor>>& params);
+ /**
+ * Returns the store's ParameterCache. This is used for validation by
+ * Configurable::init().
+ */
+ std::shared_ptr<ParameterCache> getParameterCache() const;
+
// Methods from ::android::hardware::media::c2::V1_0::IComponentStore.
virtual Return<void> createComponent(
const hidl_string& name,
@@ -98,6 +104,8 @@
protected:
sp<CachedConfigurable> mConfigurable;
+ struct StoreParameterCache;
+ std::shared_ptr<StoreParameterCache> mParameterCache;
// Does bookkeeping for an interface that has been loaded.
void onInterfaceLoaded(const std::shared_ptr<C2ComponentInterface> &intf);
diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Configurable.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Configurable.h
index 8095185..8f49a97 100644
--- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Configurable.h
+++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Configurable.h
@@ -79,6 +79,20 @@
};
/**
+ * Type for validating and caching parameters when CachedConfigurable is
+ * initialized.
+ *
+ * This is meant to be created by the ComponentStore. The purpose of abstracting
+ * this is to allow different versions of ComponentStore to work with this
+ * CachedConfigurable.
+ */
+struct ParameterCache {
+ virtual c2_status_t validate(
+ const std::vector<std::shared_ptr<C2ParamDescriptor>>&) = 0;
+ virtual ~ParameterCache() = default;
+};
+
+/**
* Implementation of the IConfigurable interface that supports caching of
* supported parameters from a supplied ComponentStore.
*
@@ -91,7 +105,8 @@
struct CachedConfigurable : public IConfigurable {
CachedConfigurable(std::unique_ptr<ConfigurableC2Intf>&& intf);
- c2_status_t init(ComponentStore* store);
+ // Populates mSupportedParams.
+ c2_status_t init(const std::shared_ptr<ParameterCache> &cache);
// Methods from ::android::hardware::media::c2::V1_0::IConfigurable
diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputBufferManager.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputBufferManager.h
index b6857d5..42fa557 100644
--- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputBufferManager.h
+++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputBufferManager.h
@@ -196,13 +196,9 @@
frameIndex(frameIndex),
bufferIndex(bufferIndex),
buffer(buffer) {}
- TrackedBuffer(const TrackedBuffer&) = default;
- bool operator<(const TrackedBuffer& other) const {
- return bufferIndex < other.bufferIndex;
- }
};
- // Map: listener -> frameIndex -> set<TrackedBuffer>.
+ // Map: listener -> frameIndex -> set<TrackedBuffer*>.
// Essentially, this is used to store triples (listener, frameIndex,
// bufferIndex) that's searchable by listener and (listener, frameIndex).
// However, the value of the innermost map is TrackedBuffer, which also
@@ -210,7 +206,7 @@
// because onBufferDestroyed() needs to know listener and frameIndex too.
typedef std::map<wp<IComponentListener>,
std::map<uint64_t,
- std::set<TrackedBuffer>>> TrackedBuffersMap;
+ std::set<TrackedBuffer*>>> TrackedBuffersMap;
// Storage for pending (unsent) death notifications for one listener.
// Each pair in member named "indices" are (frameIndex, bufferIndex) from
@@ -247,6 +243,16 @@
// Mutex for the management of all input buffers.
std::mutex mMutex;
+ // Cache for all TrackedBuffers.
+ //
+ // Whenever registerOnDestroyNotify() is called, an argument of type
+ // TrackedBuffer is created and stored into this cache.
+ // Whenever unregisterOnDestroyNotify() or onBufferDestroyed() is called,
+ // the TrackedBuffer is removed from this cache.
+ //
+ // mTrackedBuffersMap stores references to TrackedBuffers inside this cache.
+ std::set<TrackedBuffer*> mTrackedBufferCache;
+
// Tracked input buffers.
TrackedBuffersMap mTrackedBuffersMap;
diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurface.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurface.h
index 29ed7ff..062dcd9 100644
--- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurface.h
+++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurface.h
@@ -57,30 +57,26 @@
const sp<IInputSink>& sink,
connect_cb _hidl_cb) override;
+ InputSurface(
+ const std::shared_ptr<ParameterCache>& cache,
+ const std::shared_ptr<C2ReflectorHelper>& reflector,
+ const sp<HGraphicBufferProducer>& base,
+ const sp<GraphicBufferSource>& source);
+
protected:
class Interface;
class ConfigurableIntf;
- sp<ComponentStore> mStore;
+ std::shared_ptr<ParameterCache> mParameterCache;
sp<HGraphicBufferProducer> mProducer;
sp<GraphicBufferSource> mSource;
std::shared_ptr<Interface> mIntf;
sp<CachedConfigurable> mConfigurable;
- InputSurface(
- const sp<ComponentStore>& store,
- const std::shared_ptr<C2ReflectorHelper>& reflector,
- const sp<HGraphicBufferProducer>& base,
- const sp<GraphicBufferSource>& source);
-
virtual ~InputSurface() override = default;
-
- friend struct ComponentStore;
-
};
-
} // namespace utils
} // namespace V1_0
} // namespace c2
diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurfaceConnection.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurfaceConnection.h
index 758b6b2..475ce8b 100644
--- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurfaceConnection.h
+++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurfaceConnection.h
@@ -62,12 +62,12 @@
InputSurfaceConnection(
const sp<GraphicBufferSource>& source,
const std::shared_ptr<C2Component>& comp,
- const sp<ComponentStore>& store);
+ const std::shared_ptr<ParameterCache>& cache);
InputSurfaceConnection(
const sp<GraphicBufferSource>& source,
const sp<IInputSink>& sink,
- const sp<ComponentStore>& store);
+ const std::shared_ptr<ParameterCache>& cache);
bool init();
diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ClientBlockHelper.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/OutputBufferQueue.h
similarity index 78%
rename from media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ClientBlockHelper.h
rename to media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/OutputBufferQueue.h
index 0a2298c..80368f7 100644
--- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ClientBlockHelper.h
+++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/OutputBufferQueue.h
@@ -14,13 +14,15 @@
* limitations under the License.
*/
-#ifndef CLIENT_BLOCK_HELPER_H
-#define CLIENT_BLOCK_HELPER_H
+#ifndef CODEC2_HIDL_V1_0_UTILS_OUTPUT_BUFFER_QUEUE
+#define CODEC2_HIDL_V1_0_UTILS_OUTPUT_BUFFER_QUEUE
#include <gui/IGraphicBufferProducer.h>
#include <codec2/hidl/1.0/types.h>
#include <C2Work.h>
+struct C2_HIDE _C2BlockPoolData;
+
namespace android {
namespace hardware {
namespace media {
@@ -61,8 +63,16 @@
private:
- class Impl;
- std::unique_ptr<Impl> mImpl;
+ std::mutex mMutex;
+ sp<IGraphicBufferProducer> mIgbp;
+ uint32_t mGeneration;
+ uint64_t mBqId;
+ std::shared_ptr<int> mOwner;
+ // To migrate existing buffers
+ sp<GraphicBuffer> mBuffers[BufferQueueDefs::NUM_BUFFER_SLOTS]; // find a better way
+ std::weak_ptr<_C2BlockPoolData> mPoolDatas[BufferQueueDefs::NUM_BUFFER_SLOTS];
+
+ bool registerBuffer(const C2ConstGraphicBlock& block);
};
} // namespace utils
@@ -72,4 +82,4 @@
} // namespace hardware
} // namespace android
-#endif // CLIENT_BLOCK_HELPER_H
+#endif // CODEC2_HIDL_V1_0_UTILS_OUTPUT_BUFFER_QUEUE
diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
index a8a552c..9656eb4 100644
--- a/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
@@ -203,7 +203,7 @@
uint32_t mFramesReceived;
std::list<uint64_t> mFlushedIndices;
std::list<uint64_t> mTimestampUslist;
- ::android::List<outputMetaData> oBufferMetaData;
+ std::list<outputMetaData> oBufferMetaData;
C2BlockPool::local_id_t mBlockPoolId;
std::shared_ptr<C2BlockPool> mLinearPool;
@@ -547,10 +547,6 @@
if (mCompName == raw) {
bitStreamInfo[0] = 8000;
bitStreamInfo[1] = 1;
- } else if (mCompName == g711alaw || mCompName == g711mlaw) {
- // g711 test data is all 1-channel and has no embedded config info.
- bitStreamInfo[0] = 8000;
- bitStreamInfo[1] = 1;
} else {
ASSERT_NO_FATAL_FAILURE(
getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
@@ -598,7 +594,7 @@
int nSampleRate = bitStreamInfo[0];
int nChannels = bitStreamInfo[1];
std::list<uint64_t>::iterator itIn = mTimestampUslist.begin();
- android::List<outputMetaData>::iterator itOut = oBufferMetaData.begin();
+ auto itOut = oBufferMetaData.begin();
EXPECT_EQ(*itIn, itOut->timestampUs);
expTs = *itIn;
while (itOut != oBufferMetaData.end()) {
diff --git a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp
index d73b731..2f02913 100644
--- a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp
@@ -20,6 +20,13 @@
#include "media_c2_hidl_test_common.h"
+#include <android/hardware/media/c2/1.0/IComponentStore.h>
+
+void ComponentTestEnvironment::registerTestServices() {
+ registerTestService<::android::hardware::media::c2::V1_0::
+ IComponentStore>();
+}
+
// Test the codecs for NullBuffer, Empty Input Buffer with(out) flags set
void testInputBuffer(
const std::shared_ptr<android::Codec2Client::Component>& component,
diff --git a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
index db59e54..23e332a 100644
--- a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
+++ b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
@@ -17,32 +17,23 @@
#ifndef MEDIA_C2_HIDL_TEST_COMMON_H
#define MEDIA_C2_HIDL_TEST_COMMON_H
-#include <codec2/hidl/client.h>
-
-#include <android/hardware/media/c2/1.0/types.h>
-
#include <C2Component.h>
#include <C2Config.h>
+
+#include <codec2/hidl/client.h>
#include <getopt.h>
#include <hidl/HidlSupport.h>
-#include <media/stagefright/foundation/ALooper.h>
-#include <media/stagefright/foundation/Mutexed.h>
-
-using namespace ::android::hardware::media::c2::V1_0;
-using namespace ::android::hardware::media::c2::V1_0::utils;
-
-using ::android::Mutexed;
-using ::android::hardware::Void;
-using ::android::hardware::Return;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::hidl_string;
-
#include <VtsHalHidlTargetTestEnvBase.h>
#define MAX_RETRY 20
#define TIME_OUT 400ms
#define MAX_INPUT_BUFFERS 8
+using ::android::hardware::Void;
+using ::android::hardware::Return;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::hidl_string;
+
/*
* Handle Callback functions onWorkDone(), onTripped(),
* onError(), onDeath(), onFramesRendered()
@@ -114,9 +105,7 @@
typedef ::testing::VtsHalHidlTargetTestEnvBase Super;
public:
- virtual void registerTestServices() override {
- registerTestService<IComponentStore>();
- }
+ virtual void registerTestServices() override;
ComponentTestEnvironment() : res("/data/local/tmp/media/") {}
diff --git a/media/codec2/hidl/1.0/vts/functional/video/Android.bp b/media/codec2/hidl/1.0/vts/functional/video/Android.bp
index be35b02..b737323 100644
--- a/media/codec2/hidl/1.0/vts/functional/video/Android.bp
+++ b/media/codec2/hidl/1.0/vts/functional/video/Android.bp
@@ -18,6 +18,14 @@
name: "VtsHalMediaC2V1_0TargetVideoDecTest",
defaults: ["VtsHalMediaC2V1_0Defaults"],
srcs: ["VtsHalMediaC2V1_0TargetVideoDecTest.cpp"],
+ header_libs: [
+ "libnativewindow_headers",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libgui",
+ "libutils",
+ ],
}
cc_test {
diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
index 5e28750..9404aa8 100644
--- a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
@@ -28,6 +28,10 @@
#include <C2Debug.h>
#include <C2Buffer.h>
#include <C2BufferPriv.h>
+#include <gui/BufferQueue.h>
+#include <gui/IConsumerListener.h>
+#include <gui/IProducerListener.h>
+#include <system/window.h>
using android::C2AllocatorIon;
@@ -426,6 +430,48 @@
ASSERT_EQ(mDisableTest, false);
}
+TEST_F(Codec2VideoDecHidlTest, configureTunnel) {
+ description("Attempts to configure tunneling");
+ if (mDisableTest) return;
+ ALOGV("Checks if the component can be configured for tunneling");
+ native_handle_t* sidebandStream{};
+ c2_status_t err = mComponent->configureVideoTunnel(0, &sidebandStream);
+ if (err == C2_OMITTED) {
+ return;
+ }
+
+ using namespace android;
+ sp<NativeHandle> nativeHandle = NativeHandle::create(sidebandStream, true);
+
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ class DummyConsumerListener : public BnConsumerListener {
+ public:
+ DummyConsumerListener() : BnConsumerListener() {}
+ void onFrameAvailable(const BufferItem&) override {}
+ void onBuffersReleased() override {}
+ void onSidebandStreamChanged() override {}
+ };
+ consumer->consumerConnect(new DummyConsumerListener(), false);
+
+ class DummyProducerListener : public BnProducerListener {
+ public:
+ DummyProducerListener() : BnProducerListener() {}
+ virtual void onBufferReleased() override {}
+ virtual bool needsReleaseNotify() override { return false; }
+ virtual void onBuffersDiscarded(const std::vector<int32_t>&) override {}
+ };
+ IGraphicBufferProducer::QueueBufferOutput qbo{};
+ producer->connect(new DummyProducerListener(),
+ NATIVE_WINDOW_API_MEDIA,
+ false,
+ &qbo);
+
+ ASSERT_EQ(producer->setSidebandStream(nativeHandle), NO_ERROR);
+}
+
class Codec2VideoDecDecodeTest
: public Codec2VideoDecHidlTest,
public ::testing::WithParamInterface<std::pair<int32_t, bool>> {
diff --git a/media/codec2/hidl/1.1/utils/Android.bp b/media/codec2/hidl/1.1/utils/Android.bp
new file mode 100644
index 0000000..d48197b
--- /dev/null
+++ b/media/codec2/hidl/1.1/utils/Android.bp
@@ -0,0 +1,154 @@
+// DO NOT DEPEND ON THIS DIRECTLY
+// use libcodec2-hidl-client-defaults instead
+cc_library {
+ name: "libcodec2_hidl_client@1.1",
+
+ defaults: ["hidl_defaults"],
+
+ srcs: [
+ "OutputBufferQueue.cpp",
+ "types.cpp",
+ ],
+
+ header_libs: [
+ "libcodec2_internal", // private
+ ],
+
+ shared_libs: [
+ "android.hardware.media.bufferpool@2.0",
+ "android.hardware.media.c2@1.0",
+ "android.hardware.media.c2@1.1",
+ "libbase",
+ "libcodec2",
+ "libcodec2_hidl_client@1.0",
+ "libcodec2_vndk",
+ "libcutils",
+ "libgui",
+ "libhidlbase",
+ "liblog",
+ "libstagefright_bufferpool@2.0.1",
+ "libui",
+ "libutils",
+ ],
+
+ export_include_dirs: [
+ "include",
+ ],
+
+ export_shared_lib_headers: [
+ "android.hardware.media.c2@1.0",
+ "android.hardware.media.c2@1.1",
+ "libcodec2",
+ "libcodec2_hidl_client@1.0",
+ "libgui",
+ "libstagefright_bufferpool@2.0.1",
+ "libui",
+ ],
+}
+
+
+// DO NOT DEPEND ON THIS DIRECTLY
+// use libcodec2-hidl-defaults instead
+cc_library {
+ name: "libcodec2_hidl@1.1",
+ vendor_available: true,
+
+ defaults: ["hidl_defaults"],
+
+ srcs: [
+ "Component.cpp",
+ "ComponentInterface.cpp",
+ "ComponentStore.cpp",
+ "Configurable.cpp",
+ "InputBufferManager.cpp",
+ "InputSurface.cpp",
+ "InputSurfaceConnection.cpp",
+ "types.cpp",
+ ],
+
+ header_libs: [
+ "libbinder_headers",
+ "libsystem_headers",
+ "libcodec2_internal", // private
+ ],
+
+ shared_libs: [
+ "android.hardware.graphics.bufferqueue@1.0",
+ "android.hardware.graphics.bufferqueue@2.0",
+ "android.hardware.graphics.common@1.0",
+ "android.hardware.media@1.0",
+ "android.hardware.media.bufferpool@2.0",
+ "android.hardware.media.c2@1.0",
+ "android.hardware.media.c2@1.1",
+ "android.hardware.media.omx@1.0",
+ "libbase",
+ "libcodec2",
+ "libcodec2_hidl@1.0",
+ "libcodec2_vndk",
+ "libcutils",
+ "libhidlbase",
+ "liblog",
+ "libstagefright_bufferpool@2.0.1",
+ "libstagefright_bufferqueue_helper",
+ "libui",
+ "libutils",
+ ],
+
+ export_include_dirs: [
+ "include",
+ ],
+
+ export_shared_lib_headers: [
+ "android.hardware.media.c2@1.0",
+ "android.hardware.media.c2@1.1",
+ "libcodec2",
+ "libcodec2_hidl@1.0",
+ "libcodec2_vndk",
+ "libhidlbase",
+ "libstagefright_bufferpool@2.0.1",
+ "libstagefright_bufferqueue_helper",
+ "libui",
+ ],
+}
+
+// public dependency for Codec 2.0 HAL service implementations
+cc_defaults {
+ name: "libcodec2-hidl-defaults@1.1",
+ defaults: ["libcodec2-impl-defaults"],
+
+ shared_libs: [
+ "android.hardware.media.c2@1.0",
+ "android.hardware.media.c2@1.1",
+ "libcodec2_hidl@1.0",
+ "libcodec2_hidl@1.1",
+ "libcodec2_vndk",
+ "libhidlbase",
+ ],
+}
+
+// public dependency for Codec 2.0 HAL client
+cc_defaults {
+ name: "libcodec2-hidl-client-defaults@1.1",
+ defaults: ["libcodec2-impl-defaults"],
+
+ shared_libs: [
+ "android.hardware.media.c2@1.0",
+ "android.hardware.media.c2@1.1",
+ "libcodec2_hidl_client@1.0",
+ "libcodec2_hidl_client@1.1",
+ "libcodec2_vndk",
+ "libhidlbase",
+ ],
+}
+
+// Alias to the latest "defaults" for Codec 2.0 HAL service implementations
+cc_defaults {
+ name: "libcodec2-hidl-defaults",
+ defaults: ["libcodec2-hidl-defaults@1.1"],
+}
+
+// Alias to the latest "defaults" for Codec 2.0 HAL client
+cc_defaults {
+ name: "libcodec2-hidl-client-defaults",
+ defaults: ["libcodec2-hidl-client-defaults@1.1"],
+}
diff --git a/media/codec2/hidl/1.1/utils/Component.cpp b/media/codec2/hidl/1.1/utils/Component.cpp
new file mode 100644
index 0000000..ed281e6
--- /dev/null
+++ b/media/codec2/hidl/1.1/utils/Component.cpp
@@ -0,0 +1,493 @@
+/*
+ * 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_NDEBUG 0
+#define LOG_TAG "Codec2-Component@1.1"
+#include <android-base/logging.h>
+
+#include <codec2/hidl/1.1/Component.h>
+#include <codec2/hidl/1.1/ComponentStore.h>
+#include <codec2/hidl/1.1/InputBufferManager.h>
+
+#include <hidl/HidlBinderSupport.h>
+#include <utils/Timers.h>
+
+#include <C2BqBufferPriv.h>
+#include <C2Debug.h>
+#include <C2PlatformSupport.h>
+
+#include <chrono>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_1 {
+namespace utils {
+
+using namespace ::android;
+
+// ComponentListener wrapper
+struct Component::Listener : public C2Component::Listener {
+
+ Listener(const sp<Component>& component) :
+ mComponent(component),
+ mListener(component->mListener) {
+ }
+
+ virtual void onError_nb(
+ std::weak_ptr<C2Component> /* c2component */,
+ uint32_t errorCode) override {
+ sp<IComponentListener> listener = mListener.promote();
+ if (listener) {
+ Return<void> transStatus = listener->onError(Status::OK, errorCode);
+ if (!transStatus.isOk()) {
+ LOG(ERROR) << "Component::Listener::onError_nb -- "
+ << "transaction failed.";
+ }
+ }
+ }
+
+ virtual void onTripped_nb(
+ std::weak_ptr<C2Component> /* c2component */,
+ std::vector<std::shared_ptr<C2SettingResult>> c2settingResult
+ ) override {
+ sp<IComponentListener> listener = mListener.promote();
+ if (listener) {
+ hidl_vec<SettingResult> settingResults(c2settingResult.size());
+ size_t ix = 0;
+ for (const std::shared_ptr<C2SettingResult> &c2result :
+ c2settingResult) {
+ if (c2result) {
+ if (!objcpy(&settingResults[ix++], *c2result)) {
+ break;
+ }
+ }
+ }
+ settingResults.resize(ix);
+ Return<void> transStatus = listener->onTripped(settingResults);
+ if (!transStatus.isOk()) {
+ LOG(ERROR) << "Component::Listener::onTripped_nb -- "
+ << "transaction failed.";
+ }
+ }
+ }
+
+ virtual void onWorkDone_nb(
+ std::weak_ptr<C2Component> /* c2component */,
+ std::list<std::unique_ptr<C2Work>> c2workItems) override {
+ for (const std::unique_ptr<C2Work>& work : c2workItems) {
+ if (work) {
+ if (work->worklets.empty()
+ || !work->worklets.back()
+ || (work->worklets.back()->output.flags &
+ C2FrameData::FLAG_INCOMPLETE) == 0) {
+ InputBufferManager::
+ unregisterFrameData(mListener, work->input);
+ }
+ }
+ }
+
+ sp<IComponentListener> listener = mListener.promote();
+ if (listener) {
+ WorkBundle workBundle;
+
+ sp<Component> strongComponent = mComponent.promote();
+ beginTransferBufferQueueBlocks(c2workItems, true);
+ if (!objcpy(&workBundle, c2workItems, strongComponent ?
+ &strongComponent->mBufferPoolSender : nullptr)) {
+ LOG(ERROR) << "Component::Listener::onWorkDone_nb -- "
+ << "received corrupted work items.";
+ endTransferBufferQueueBlocks(c2workItems, false, true);
+ return;
+ }
+ Return<void> transStatus = listener->onWorkDone(workBundle);
+ if (!transStatus.isOk()) {
+ LOG(ERROR) << "Component::Listener::onWorkDone_nb -- "
+ << "transaction failed.";
+ endTransferBufferQueueBlocks(c2workItems, false, true);
+ return;
+ }
+ endTransferBufferQueueBlocks(c2workItems, true, true);
+ }
+ }
+
+protected:
+ wp<Component> mComponent;
+ wp<IComponentListener> mListener;
+};
+
+// Component::Sink
+struct Component::Sink : public IInputSink {
+ std::shared_ptr<Component> mComponent;
+ sp<IConfigurable> mConfigurable;
+
+ virtual Return<Status> queue(const WorkBundle& workBundle) override {
+ return mComponent->queue(workBundle);
+ }
+
+ virtual Return<sp<IConfigurable>> getConfigurable() override {
+ return mConfigurable;
+ }
+
+ Sink(const std::shared_ptr<Component>& component);
+ virtual ~Sink() override;
+
+ // Process-wide map: Component::Sink -> C2Component.
+ static std::mutex sSink2ComponentMutex;
+ static std::map<IInputSink*, std::weak_ptr<C2Component>> sSink2Component;
+
+ static std::shared_ptr<C2Component> findLocalComponent(
+ const sp<IInputSink>& sink);
+};
+
+std::mutex
+ Component::Sink::sSink2ComponentMutex{};
+std::map<IInputSink*, std::weak_ptr<C2Component>>
+ Component::Sink::sSink2Component{};
+
+Component::Sink::Sink(const std::shared_ptr<Component>& component)
+ : mComponent{component},
+ mConfigurable{[&component]() -> sp<IConfigurable> {
+ Return<sp<IComponentInterface>> ret1 = component->getInterface();
+ if (!ret1.isOk()) {
+ LOG(ERROR) << "Sink::Sink -- component's transaction failed.";
+ return nullptr;
+ }
+ Return<sp<IConfigurable>> ret2 =
+ static_cast<sp<IComponentInterface>>(ret1)->
+ getConfigurable();
+ if (!ret2.isOk()) {
+ LOG(ERROR) << "Sink::Sink -- interface's transaction failed.";
+ return nullptr;
+ }
+ return static_cast<sp<IConfigurable>>(ret2);
+ }()} {
+ std::lock_guard<std::mutex> lock(sSink2ComponentMutex);
+ sSink2Component.emplace(this, component->mComponent);
+}
+
+Component::Sink::~Sink() {
+ std::lock_guard<std::mutex> lock(sSink2ComponentMutex);
+ sSink2Component.erase(this);
+}
+
+std::shared_ptr<C2Component> Component::Sink::findLocalComponent(
+ const sp<IInputSink>& sink) {
+ std::lock_guard<std::mutex> lock(sSink2ComponentMutex);
+ auto i = sSink2Component.find(sink.get());
+ if (i == sSink2Component.end()) {
+ return nullptr;
+ }
+ return i->second.lock();
+}
+
+// Component
+Component::Component(
+ const std::shared_ptr<C2Component>& component,
+ const sp<IComponentListener>& listener,
+ const sp<ComponentStore>& store,
+ const sp<::android::hardware::media::bufferpool::V2_0::
+ IClientManager>& clientPoolManager)
+ : mComponent{component},
+ mInterface{new ComponentInterface(component->intf(),
+ store->getParameterCache())},
+ mListener{listener},
+ mStore{store},
+ mBufferPoolSender{clientPoolManager} {
+ // Retrieve supported parameters from store
+ // TODO: We could cache this per component/interface type
+ mInit = mInterface->status();
+}
+
+c2_status_t Component::status() const {
+ return mInit;
+}
+
+// Methods from ::android::hardware::media::c2::V1_1::IComponent
+Return<Status> Component::queue(const WorkBundle& workBundle) {
+ std::list<std::unique_ptr<C2Work>> c2works;
+
+ if (!objcpy(&c2works, workBundle)) {
+ return Status::CORRUPTED;
+ }
+
+ // Register input buffers.
+ for (const std::unique_ptr<C2Work>& work : c2works) {
+ if (work) {
+ InputBufferManager::
+ registerFrameData(mListener, work->input);
+ }
+ }
+
+ return static_cast<Status>(mComponent->queue_nb(&c2works));
+}
+
+Return<void> Component::flush(flush_cb _hidl_cb) {
+ std::list<std::unique_ptr<C2Work>> c2flushedWorks;
+ c2_status_t c2res = mComponent->flush_sm(
+ C2Component::FLUSH_COMPONENT,
+ &c2flushedWorks);
+
+ // Unregister input buffers.
+ for (const std::unique_ptr<C2Work>& work : c2flushedWorks) {
+ if (work) {
+ if (work->worklets.empty()
+ || !work->worklets.back()
+ || (work->worklets.back()->output.flags &
+ C2FrameData::FLAG_INCOMPLETE) == 0) {
+ InputBufferManager::
+ unregisterFrameData(mListener, work->input);
+ }
+ }
+ }
+
+ WorkBundle flushedWorkBundle;
+ Status res = static_cast<Status>(c2res);
+ beginTransferBufferQueueBlocks(c2flushedWorks, true);
+ if (c2res == C2_OK) {
+ if (!objcpy(&flushedWorkBundle, c2flushedWorks, &mBufferPoolSender)) {
+ res = Status::CORRUPTED;
+ }
+ }
+ _hidl_cb(res, flushedWorkBundle);
+ endTransferBufferQueueBlocks(c2flushedWorks, true, true);
+ return Void();
+}
+
+Return<Status> Component::drain(bool withEos) {
+ return static_cast<Status>(mComponent->drain_nb(withEos ?
+ C2Component::DRAIN_COMPONENT_WITH_EOS :
+ C2Component::DRAIN_COMPONENT_NO_EOS));
+}
+
+Return<Status> Component::setOutputSurface(
+ uint64_t blockPoolId,
+ const sp<HGraphicBufferProducer2>& surface) {
+ std::shared_ptr<C2BlockPool> pool;
+ GetCodec2BlockPool(blockPoolId, mComponent, &pool);
+ if (pool && pool->getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) {
+ std::shared_ptr<C2BufferQueueBlockPool> bqPool =
+ std::static_pointer_cast<C2BufferQueueBlockPool>(pool);
+ C2BufferQueueBlockPool::OnRenderCallback cb =
+ [this](uint64_t producer, int32_t slot, int64_t nsecs) {
+ // TODO: batch this
+ hidl_vec<IComponentListener::RenderedFrame> rendered;
+ rendered.resize(1);
+ rendered[0] = { producer, slot, nsecs };
+ (void)mListener->onFramesRendered(rendered).isOk();
+ };
+ if (bqPool) {
+ bqPool->setRenderCallback(cb);
+ bqPool->configureProducer(surface);
+ }
+ }
+ return Status::OK;
+}
+
+Return<void> Component::connectToInputSurface(
+ const sp<IInputSurface>& inputSurface,
+ connectToInputSurface_cb _hidl_cb) {
+ Status status;
+ sp<IInputSurfaceConnection> connection;
+ auto transStatus = inputSurface->connect(
+ asInputSink(),
+ [&status, &connection](
+ Status s, const sp<IInputSurfaceConnection>& c) {
+ status = s;
+ connection = c;
+ }
+ );
+ _hidl_cb(status, connection);
+ return Void();
+}
+
+Return<void> Component::connectToOmxInputSurface(
+ const sp<HGraphicBufferProducer1>& producer,
+ const sp<::android::hardware::media::omx::V1_0::
+ IGraphicBufferSource>& source,
+ connectToOmxInputSurface_cb _hidl_cb) {
+ (void)producer;
+ (void)source;
+ (void)_hidl_cb;
+ return Void();
+}
+
+Return<Status> Component::disconnectFromInputSurface() {
+ // TODO implement
+ return Status::OK;
+}
+
+namespace /* unnamed */ {
+
+struct BlockPoolIntf : public ConfigurableC2Intf {
+ BlockPoolIntf(const std::shared_ptr<C2BlockPool>& pool)
+ : ConfigurableC2Intf{
+ "C2BlockPool:" +
+ (pool ? std::to_string(pool->getLocalId()) : "null"),
+ 0},
+ mPool{pool} {
+ }
+
+ virtual c2_status_t config(
+ const std::vector<C2Param*>& params,
+ c2_blocking_t mayBlock,
+ std::vector<std::unique_ptr<C2SettingResult>>* const failures
+ ) override {
+ (void)params;
+ (void)mayBlock;
+ (void)failures;
+ return C2_OK;
+ }
+
+ virtual c2_status_t query(
+ const std::vector<C2Param::Index>& indices,
+ c2_blocking_t mayBlock,
+ std::vector<std::unique_ptr<C2Param>>* const params
+ ) const override {
+ (void)indices;
+ (void)mayBlock;
+ (void)params;
+ return C2_OK;
+ }
+
+ virtual c2_status_t querySupportedParams(
+ std::vector<std::shared_ptr<C2ParamDescriptor>>* const params
+ ) const override {
+ (void)params;
+ return C2_OK;
+ }
+
+ virtual c2_status_t querySupportedValues(
+ std::vector<C2FieldSupportedValuesQuery>& fields,
+ c2_blocking_t mayBlock) const override {
+ (void)fields;
+ (void)mayBlock;
+ return C2_OK;
+ }
+
+protected:
+ std::shared_ptr<C2BlockPool> mPool;
+};
+
+} // unnamed namespace
+
+Return<void> Component::createBlockPool(
+ uint32_t allocatorId,
+ createBlockPool_cb _hidl_cb) {
+ std::shared_ptr<C2BlockPool> blockPool;
+ c2_status_t status = CreateCodec2BlockPool(
+ static_cast<C2PlatformAllocatorStore::id_t>(allocatorId),
+ mComponent,
+ &blockPool);
+ if (status != C2_OK) {
+ blockPool = nullptr;
+ }
+ if (blockPool) {
+ mBlockPoolsMutex.lock();
+ mBlockPools.emplace(blockPool->getLocalId(), blockPool);
+ mBlockPoolsMutex.unlock();
+ } else if (status == C2_OK) {
+ status = C2_CORRUPTED;
+ }
+
+ _hidl_cb(static_cast<Status>(status),
+ blockPool ? blockPool->getLocalId() : 0,
+ new CachedConfigurable(
+ std::make_unique<BlockPoolIntf>(blockPool)));
+ return Void();
+}
+
+Return<Status> Component::destroyBlockPool(uint64_t blockPoolId) {
+ std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
+ return mBlockPools.erase(blockPoolId) == 1 ?
+ Status::OK : Status::CORRUPTED;
+}
+
+Return<Status> Component::start() {
+ return static_cast<Status>(mComponent->start());
+}
+
+Return<Status> Component::stop() {
+ InputBufferManager::unregisterFrameData(mListener);
+ return static_cast<Status>(mComponent->stop());
+}
+
+Return<Status> Component::reset() {
+ Status status = static_cast<Status>(mComponent->reset());
+ {
+ std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
+ mBlockPools.clear();
+ }
+ InputBufferManager::unregisterFrameData(mListener);
+ return status;
+}
+
+Return<Status> Component::release() {
+ Status status = static_cast<Status>(mComponent->release());
+ {
+ std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
+ mBlockPools.clear();
+ }
+ InputBufferManager::unregisterFrameData(mListener);
+ return status;
+}
+
+Return<sp<IComponentInterface>> Component::getInterface() {
+ return sp<IComponentInterface>(mInterface);
+}
+
+Return<sp<IInputSink>> Component::asInputSink() {
+ std::lock_guard<std::mutex> lock(mSinkMutex);
+ if (!mSink) {
+ mSink = new Sink(shared_from_this());
+ }
+ return {mSink};
+}
+
+Return<void> Component::configureVideoTunnel(
+ uint32_t avSyncHwId, configureVideoTunnel_cb _hidl_cb) {
+ (void)avSyncHwId;
+ _hidl_cb(Status::OMITTED, hidl_handle{});
+ return Void();
+}
+
+std::shared_ptr<C2Component> Component::findLocalComponent(
+ const sp<IInputSink>& sink) {
+ return Component::Sink::findLocalComponent(sink);
+}
+
+void Component::initListener(const sp<Component>& self) {
+ std::shared_ptr<C2Component::Listener> c2listener =
+ std::make_shared<Listener>(self);
+ c2_status_t res = mComponent->setListener_vb(c2listener, C2_DONT_BLOCK);
+ if (res != C2_OK) {
+ mInit = res;
+ }
+}
+
+Component::~Component() {
+ InputBufferManager::unregisterFrameData(mListener);
+ mStore->reportComponentDeath(this);
+}
+
+} // namespace utils
+} // namespace V1_1
+} // namespace c2
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/codec2/hidl/1.1/utils/ComponentInterface.cpp b/media/codec2/hidl/1.1/utils/ComponentInterface.cpp
new file mode 100644
index 0000000..ccc30fe
--- /dev/null
+++ b/media/codec2/hidl/1.1/utils/ComponentInterface.cpp
@@ -0,0 +1,17 @@
+/*
+ * 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.
+ */
+
+#include <codec2/hidl/1.1/ComponentInterface.h>
diff --git a/media/codec2/hidl/1.1/utils/ComponentStore.cpp b/media/codec2/hidl/1.1/utils/ComponentStore.cpp
new file mode 100644
index 0000000..225cd09
--- /dev/null
+++ b/media/codec2/hidl/1.1/utils/ComponentStore.cpp
@@ -0,0 +1,499 @@
+/*
+ * 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_NDEBUG 0
+#define LOG_TAG "Codec2-ComponentStore@1.1"
+#include <android-base/logging.h>
+
+#include <codec2/hidl/1.1/ComponentStore.h>
+#include <codec2/hidl/1.1/InputSurface.h>
+#include <codec2/hidl/1.1/types.h>
+
+#include <android-base/file.h>
+#include <media/stagefright/bqhelper/GraphicBufferSource.h>
+#include <utils/Errors.h>
+
+#include <C2PlatformSupport.h>
+#include <util/C2InterfaceHelper.h>
+
+#include <chrono>
+#include <ctime>
+#include <iomanip>
+#include <ostream>
+#include <sstream>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_1 {
+namespace utils {
+
+using namespace ::android;
+using ::android::GraphicBufferSource;
+using namespace ::android::hardware::media::bufferpool::V2_0::implementation;
+
+namespace /* unnamed */ {
+
+struct StoreIntf : public ConfigurableC2Intf {
+ StoreIntf(const std::shared_ptr<C2ComponentStore>& store)
+ : ConfigurableC2Intf{store ? store->getName() : "", 0},
+ mStore{store} {
+ }
+
+ virtual c2_status_t config(
+ const std::vector<C2Param*> ¶ms,
+ c2_blocking_t mayBlock,
+ std::vector<std::unique_ptr<C2SettingResult>> *const failures
+ ) override {
+ // Assume all params are blocking
+ // TODO: Filter for supported params
+ if (mayBlock == C2_DONT_BLOCK && params.size() != 0) {
+ return C2_BLOCKING;
+ }
+ return mStore->config_sm(params, failures);
+ }
+
+ virtual c2_status_t query(
+ const std::vector<C2Param::Index> &indices,
+ c2_blocking_t mayBlock,
+ std::vector<std::unique_ptr<C2Param>> *const params) const override {
+ // Assume all params are blocking
+ // TODO: Filter for supported params
+ if (mayBlock == C2_DONT_BLOCK && indices.size() != 0) {
+ return C2_BLOCKING;
+ }
+ return mStore->query_sm({}, indices, params);
+ }
+
+ virtual c2_status_t querySupportedParams(
+ std::vector<std::shared_ptr<C2ParamDescriptor>> *const params
+ ) const override {
+ return mStore->querySupportedParams_nb(params);
+ }
+
+ virtual c2_status_t querySupportedValues(
+ std::vector<C2FieldSupportedValuesQuery> &fields,
+ c2_blocking_t mayBlock) const override {
+ // Assume all params are blocking
+ // TODO: Filter for supported params
+ if (mayBlock == C2_DONT_BLOCK && fields.size() != 0) {
+ return C2_BLOCKING;
+ }
+ return mStore->querySupportedValues_sm(fields);
+ }
+
+protected:
+ std::shared_ptr<C2ComponentStore> mStore;
+};
+
+} // unnamed namespace
+
+struct ComponentStore::StoreParameterCache : public ParameterCache {
+ std::mutex mStoreMutex;
+ ComponentStore* mStore;
+
+ StoreParameterCache(ComponentStore* store): mStore{store} {
+ }
+
+ virtual c2_status_t validate(
+ const std::vector<std::shared_ptr<C2ParamDescriptor>>& params
+ ) override {
+ std::scoped_lock _lock(mStoreMutex);
+ return mStore ? mStore->validateSupportedParams(params) : C2_NO_INIT;
+ }
+
+ void onStoreDestroyed() {
+ std::scoped_lock _lock(mStoreMutex);
+ mStore = nullptr;
+ }
+};
+
+ComponentStore::ComponentStore(const std::shared_ptr<C2ComponentStore>& store)
+ : mConfigurable{new CachedConfigurable(std::make_unique<StoreIntf>(store))},
+ mParameterCache{std::make_shared<StoreParameterCache>(this)},
+ mStore{store} {
+
+ std::shared_ptr<C2ComponentStore> platformStore = android::GetCodec2PlatformComponentStore();
+ SetPreferredCodec2ComponentStore(store);
+
+ // Retrieve struct descriptors
+ mParamReflector = mStore->getParamReflector();
+
+ // Retrieve supported parameters from store
+ using namespace std::placeholders;
+ mInit = mConfigurable->init(mParameterCache);
+}
+
+ComponentStore::~ComponentStore() {
+ mParameterCache->onStoreDestroyed();
+}
+
+c2_status_t ComponentStore::status() const {
+ return mInit;
+}
+
+c2_status_t ComponentStore::validateSupportedParams(
+ const std::vector<std::shared_ptr<C2ParamDescriptor>>& params) {
+ c2_status_t res = C2_OK;
+
+ for (const std::shared_ptr<C2ParamDescriptor> &desc : params) {
+ if (!desc) {
+ // All descriptors should be valid
+ res = res ? res : C2_BAD_VALUE;
+ continue;
+ }
+ C2Param::CoreIndex coreIndex = desc->index().coreIndex();
+ std::lock_guard<std::mutex> lock(mStructDescriptorsMutex);
+ auto it = mStructDescriptors.find(coreIndex);
+ if (it == mStructDescriptors.end()) {
+ std::shared_ptr<C2StructDescriptor> structDesc =
+ mParamReflector->describe(coreIndex);
+ if (!structDesc) {
+ // All supported params must be described
+ res = C2_BAD_INDEX;
+ }
+ mStructDescriptors.insert({ coreIndex, structDesc });
+ }
+ }
+ return res;
+}
+
+std::shared_ptr<ParameterCache> ComponentStore::getParameterCache() const {
+ return mParameterCache;
+}
+
+// Methods from ::android::hardware::media::c2::V1_0::IComponentStore
+Return<void> ComponentStore::createComponent(
+ const hidl_string& name,
+ const sp<IComponentListener>& listener,
+ const sp<IClientManager>& pool,
+ createComponent_cb _hidl_cb) {
+
+ sp<Component> component;
+ std::shared_ptr<C2Component> c2component;
+ Status status = static_cast<Status>(
+ mStore->createComponent(name, &c2component));
+
+ if (status == Status::OK) {
+ onInterfaceLoaded(c2component->intf());
+ component = new Component(c2component, listener, this, pool);
+ if (!component) {
+ status = Status::CORRUPTED;
+ } else {
+ reportComponentBirth(component.get());
+ if (component->status() != C2_OK) {
+ status = static_cast<Status>(component->status());
+ } else {
+ component->initListener(component);
+ if (component->status() != C2_OK) {
+ status = static_cast<Status>(component->status());
+ }
+ }
+ }
+ }
+ _hidl_cb(status, component);
+ return Void();
+}
+
+Return<void> ComponentStore::createInterface(
+ const hidl_string& name,
+ createInterface_cb _hidl_cb) {
+ std::shared_ptr<C2ComponentInterface> c2interface;
+ c2_status_t res = mStore->createInterface(name, &c2interface);
+ sp<IComponentInterface> interface;
+ if (res == C2_OK) {
+ onInterfaceLoaded(c2interface);
+ interface = new ComponentInterface(c2interface, mParameterCache);
+ }
+ _hidl_cb(static_cast<Status>(res), interface);
+ return Void();
+}
+
+Return<void> ComponentStore::listComponents(listComponents_cb _hidl_cb) {
+ std::vector<std::shared_ptr<const C2Component::Traits>> c2traits =
+ mStore->listComponents();
+ hidl_vec<IComponentStore::ComponentTraits> traits(c2traits.size());
+ size_t ix = 0;
+ for (const std::shared_ptr<const C2Component::Traits> &c2trait : c2traits) {
+ if (c2trait) {
+ if (objcpy(&traits[ix], *c2trait)) {
+ ++ix;
+ } else {
+ break;
+ }
+ }
+ }
+ traits.resize(ix);
+ _hidl_cb(Status::OK, traits);
+ return Void();
+}
+
+Return<void> ComponentStore::createInputSurface(createInputSurface_cb _hidl_cb) {
+ sp<GraphicBufferSource> source = new GraphicBufferSource();
+ if (source->initCheck() != OK) {
+ _hidl_cb(Status::CORRUPTED, nullptr);
+ return Void();
+ }
+ using namespace std::placeholders;
+ sp<InputSurface> inputSurface = new InputSurface(
+ mParameterCache,
+ std::make_shared<C2ReflectorHelper>(),
+ source->getHGraphicBufferProducer(),
+ source);
+ _hidl_cb(inputSurface ? Status::OK : Status::NO_MEMORY,
+ inputSurface);
+ return Void();
+}
+
+void ComponentStore::onInterfaceLoaded(const std::shared_ptr<C2ComponentInterface> &intf) {
+ // invalidate unsupported struct descriptors if a new interface is loaded as it may have
+ // exposed new descriptors
+ std::lock_guard<std::mutex> lock(mStructDescriptorsMutex);
+ if (!mLoadedInterfaces.count(intf->getName())) {
+ mUnsupportedStructDescriptors.clear();
+ mLoadedInterfaces.emplace(intf->getName());
+ }
+}
+
+Return<void> ComponentStore::getStructDescriptors(
+ const hidl_vec<uint32_t>& indices,
+ getStructDescriptors_cb _hidl_cb) {
+ hidl_vec<StructDescriptor> descriptors(indices.size());
+ size_t dstIx = 0;
+ Status res = Status::OK;
+ for (size_t srcIx = 0; srcIx < indices.size(); ++srcIx) {
+ std::lock_guard<std::mutex> lock(mStructDescriptorsMutex);
+ const C2Param::CoreIndex coreIndex = C2Param::CoreIndex(indices[srcIx]).coreIndex();
+ const auto item = mStructDescriptors.find(coreIndex);
+ if (item == mStructDescriptors.end()) {
+ // not in the cache, and not known to be unsupported, query local reflector
+ if (!mUnsupportedStructDescriptors.count(coreIndex)) {
+ std::shared_ptr<C2StructDescriptor> structDesc =
+ mParamReflector->describe(coreIndex);
+ if (!structDesc) {
+ mUnsupportedStructDescriptors.emplace(coreIndex);
+ } else {
+ mStructDescriptors.insert({ coreIndex, structDesc });
+ if (objcpy(&descriptors[dstIx], *structDesc)) {
+ ++dstIx;
+ continue;
+ }
+ res = Status::CORRUPTED;
+ break;
+ }
+ }
+ res = Status::NOT_FOUND;
+ } else if (item->second) {
+ if (objcpy(&descriptors[dstIx], *item->second)) {
+ ++dstIx;
+ continue;
+ }
+ res = Status::CORRUPTED;
+ break;
+ } else {
+ res = Status::NO_MEMORY;
+ break;
+ }
+ }
+ descriptors.resize(dstIx);
+ _hidl_cb(res, descriptors);
+ return Void();
+}
+
+Return<sp<IClientManager>> ComponentStore::getPoolClientManager() {
+ return ClientManager::getInstance();
+}
+
+Return<Status> ComponentStore::copyBuffer(const Buffer& src, const Buffer& dst) {
+ // TODO implement
+ (void)src;
+ (void)dst;
+ return Status::OMITTED;
+}
+
+Return<sp<IConfigurable>> ComponentStore::getConfigurable() {
+ return mConfigurable;
+}
+
+// Methods from ::android::hardware::media::c2::V1_1::IComponentStore
+Return<void> ComponentStore::createComponent_1_1(
+ const hidl_string& name,
+ const sp<IComponentListener>& listener,
+ const sp<IClientManager>& pool,
+ createComponent_1_1_cb _hidl_cb) {
+
+ sp<Component> component;
+ std::shared_ptr<C2Component> c2component;
+ Status status = static_cast<Status>(
+ mStore->createComponent(name, &c2component));
+
+ if (status == Status::OK) {
+ onInterfaceLoaded(c2component->intf());
+ component = new Component(c2component, listener, this, pool);
+ if (!component) {
+ status = Status::CORRUPTED;
+ } else {
+ reportComponentBirth(component.get());
+ if (component->status() != C2_OK) {
+ status = static_cast<Status>(component->status());
+ } else {
+ component->initListener(component);
+ if (component->status() != C2_OK) {
+ status = static_cast<Status>(component->status());
+ }
+ }
+ }
+ }
+ _hidl_cb(status, component);
+ return Void();
+}
+
+// Called from createComponent() after a successful creation of `component`.
+void ComponentStore::reportComponentBirth(Component* component) {
+ ComponentStatus componentStatus;
+ componentStatus.c2Component = component->mComponent;
+ componentStatus.birthTime = std::chrono::system_clock::now();
+
+ std::lock_guard<std::mutex> lock(mComponentRosterMutex);
+ mComponentRoster.emplace(component, componentStatus);
+}
+
+// Called from within the destructor of `component`. No virtual function calls
+// are made on `component` here.
+void ComponentStore::reportComponentDeath(Component* component) {
+ std::lock_guard<std::mutex> lock(mComponentRosterMutex);
+ mComponentRoster.erase(component);
+}
+
+// Dumps component traits.
+std::ostream& ComponentStore::dump(
+ std::ostream& out,
+ const std::shared_ptr<const C2Component::Traits>& comp) {
+
+ constexpr const char indent[] = " ";
+
+ out << indent << "name: " << comp->name << std::endl;
+ out << indent << "domain: " << comp->domain << std::endl;
+ out << indent << "kind: " << comp->kind << std::endl;
+ out << indent << "rank: " << comp->rank << std::endl;
+ out << indent << "mediaType: " << comp->mediaType << std::endl;
+ out << indent << "aliases:";
+ for (const auto& alias : comp->aliases) {
+ out << ' ' << alias;
+ }
+ out << std::endl;
+
+ return out;
+}
+
+// Dumps component status.
+std::ostream& ComponentStore::dump(
+ std::ostream& out,
+ ComponentStatus& compStatus) {
+
+ constexpr const char indent[] = " ";
+
+ // Print birth time.
+ std::chrono::milliseconds ms =
+ std::chrono::duration_cast<std::chrono::milliseconds>(
+ compStatus.birthTime.time_since_epoch());
+ std::time_t birthTime = std::chrono::system_clock::to_time_t(
+ compStatus.birthTime);
+ std::tm tm = *std::localtime(&birthTime);
+ out << indent << "Creation time: "
+ << std::put_time(&tm, "%Y-%m-%d %H:%M:%S")
+ << '.' << std::setfill('0') << std::setw(3) << ms.count() % 1000
+ << std::endl;
+
+ // Print name and id.
+ std::shared_ptr<C2ComponentInterface> intf = compStatus.c2Component->intf();
+ if (!intf) {
+ out << indent << "Unknown component -- null interface" << std::endl;
+ return out;
+ }
+ out << indent << "Name: " << intf->getName() << std::endl;
+ out << indent << "Id: " << intf->getId() << std::endl;
+
+ return out;
+}
+
+// Dumps information when lshal is called.
+Return<void> ComponentStore::debug(
+ const hidl_handle& handle,
+ const hidl_vec<hidl_string>& /* args */) {
+ LOG(INFO) << "debug -- dumping...";
+ const native_handle_t *h = handle.getNativeHandle();
+ if (!h || h->numFds != 1) {
+ LOG(ERROR) << "debug -- dumping failed -- "
+ "invalid file descriptor to dump to";
+ return Void();
+ }
+ std::ostringstream out;
+
+ { // Populate "out".
+
+ constexpr const char indent[] = " ";
+
+ // Show name.
+ out << "Beginning of dump -- C2ComponentStore: "
+ << mStore->getName() << std::endl << std::endl;
+
+ // Retrieve the list of supported components.
+ std::vector<std::shared_ptr<const C2Component::Traits>> traitsList =
+ mStore->listComponents();
+
+ // Dump the traits of supported components.
+ out << indent << "Supported components:" << std::endl << std::endl;
+ if (traitsList.size() == 0) {
+ out << indent << indent << "NONE" << std::endl << std::endl;
+ } else {
+ for (const auto& traits : traitsList) {
+ dump(out, traits) << std::endl;
+ }
+ }
+
+ // Dump active components.
+ {
+ out << indent << "Active components:" << std::endl << std::endl;
+ std::lock_guard<std::mutex> lock(mComponentRosterMutex);
+ if (mComponentRoster.size() == 0) {
+ out << indent << indent << "NONE" << std::endl << std::endl;
+ } else {
+ for (auto& pair : mComponentRoster) {
+ dump(out, pair.second) << std::endl;
+ }
+ }
+ }
+
+ out << "End of dump -- C2ComponentStore: "
+ << mStore->getName() << std::endl;
+ }
+
+ if (!android::base::WriteStringToFd(out.str(), h->data[0])) {
+ PLOG(WARNING) << "debug -- dumping failed -- write()";
+ } else {
+ LOG(INFO) << "debug -- dumping succeeded";
+ }
+ return Void();
+}
+
+} // namespace utils
+} // namespace V1_1
+} // namespace c2
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/codec2/hidl/1.1/utils/Configurable.cpp b/media/codec2/hidl/1.1/utils/Configurable.cpp
new file mode 100644
index 0000000..1f3b2c7
--- /dev/null
+++ b/media/codec2/hidl/1.1/utils/Configurable.cpp
@@ -0,0 +1,17 @@
+/*
+ * 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.
+ */
+
+#include <codec2/hidl/1.1/Configurable.h>
diff --git a/media/codec2/hidl/1.1/utils/InputBufferManager.cpp b/media/codec2/hidl/1.1/utils/InputBufferManager.cpp
new file mode 100644
index 0000000..45bfc86
--- /dev/null
+++ b/media/codec2/hidl/1.1/utils/InputBufferManager.cpp
@@ -0,0 +1,17 @@
+/*
+ * 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.
+ */
+
+#include <codec2/hidl/1.1/InputBufferManager.h>
diff --git a/media/codec2/hidl/1.1/utils/InputSurface.cpp b/media/codec2/hidl/1.1/utils/InputSurface.cpp
new file mode 100644
index 0000000..ce40494
--- /dev/null
+++ b/media/codec2/hidl/1.1/utils/InputSurface.cpp
@@ -0,0 +1,17 @@
+/*
+ * 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.
+ */
+
+#include <codec2/hidl/1.1/InputSurface.h>
diff --git a/media/codec2/hidl/1.1/utils/InputSurfaceConnection.cpp b/media/codec2/hidl/1.1/utils/InputSurfaceConnection.cpp
new file mode 100644
index 0000000..32154a7
--- /dev/null
+++ b/media/codec2/hidl/1.1/utils/InputSurfaceConnection.cpp
@@ -0,0 +1,17 @@
+/*
+ * 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.
+ */
+
+#include <codec2/hidl/1.1/InputSurfaceConnection.h>
diff --git a/media/codec2/hidl/1.1/utils/OutputBufferQueue.cpp b/media/codec2/hidl/1.1/utils/OutputBufferQueue.cpp
new file mode 100644
index 0000000..65756e8
--- /dev/null
+++ b/media/codec2/hidl/1.1/utils/OutputBufferQueue.cpp
@@ -0,0 +1,17 @@
+/*
+ * 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.
+ */
+
+#include <codec2/hidl/1.1/OutputBufferQueue.h>
diff --git a/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/Component.h b/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/Component.h
new file mode 100644
index 0000000..16c81d4
--- /dev/null
+++ b/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/Component.h
@@ -0,0 +1,149 @@
+/*
+ * 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 CODEC2_HIDL_V1_1_UTILS_COMPONENT_H
+#define CODEC2_HIDL_V1_1_UTILS_COMPONENT_H
+
+#include <android/hardware/media/bufferpool/2.0/IClientManager.h>
+#include <android/hardware/media/c2/1.1/IComponent.h>
+#include <android/hardware/media/c2/1.0/IComponentInterface.h>
+#include <android/hardware/media/c2/1.0/IComponentListener.h>
+#include <android/hardware/media/c2/1.1/IComponentStore.h>
+#include <android/hardware/media/c2/1.0/IInputSink.h>
+#include <codec2/hidl/1.1/ComponentInterface.h>
+#include <codec2/hidl/1.1/Configurable.h>
+#include <codec2/hidl/1.1/types.h>
+#include <hidl/Status.h>
+#include <hwbinder/IBinder.h>
+
+#include <C2Component.h>
+#include <C2Buffer.h>
+#include <C2.h>
+
+#include <map>
+#include <memory>
+#include <mutex>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_1 {
+
+using ::android::hardware::media::c2::V1_1::IComponent;
+using ::android::hardware::media::c2::V1_0::IComponentListener;
+
+namespace utils {
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::IBinder;
+using ::android::sp;
+using ::android::wp;
+
+struct ComponentStore;
+
+struct Component : public IComponent,
+ public std::enable_shared_from_this<Component> {
+ Component(
+ const std::shared_ptr<C2Component>&,
+ const sp<IComponentListener>& listener,
+ const sp<ComponentStore>& store,
+ const sp<::android::hardware::media::bufferpool::V2_0::
+ IClientManager>& clientPoolManager);
+ c2_status_t status() const;
+
+ typedef ::android::hardware::graphics::bufferqueue::V1_0::
+ IGraphicBufferProducer HGraphicBufferProducer1;
+ typedef ::android::hardware::graphics::bufferqueue::V2_0::
+ IGraphicBufferProducer HGraphicBufferProducer2;
+
+ // Methods from IComponent follow.
+ virtual Return<Status> queue(const WorkBundle& workBundle) override;
+ virtual Return<void> flush(flush_cb _hidl_cb) override;
+ virtual Return<Status> drain(bool withEos) override;
+ virtual Return<Status> setOutputSurface(
+ uint64_t blockPoolId,
+ const sp<HGraphicBufferProducer2>& surface) override;
+ virtual Return<void> connectToInputSurface(
+ const sp<IInputSurface>& inputSurface,
+ connectToInputSurface_cb _hidl_cb) override;
+ virtual Return<void> connectToOmxInputSurface(
+ const sp<HGraphicBufferProducer1>& producer,
+ const sp<::android::hardware::media::omx::V1_0::
+ IGraphicBufferSource>& source,
+ connectToOmxInputSurface_cb _hidl_cb) override;
+ virtual Return<Status> disconnectFromInputSurface() override;
+ virtual Return<void> createBlockPool(
+ uint32_t allocatorId,
+ createBlockPool_cb _hidl_cb) override;
+ virtual Return<Status> destroyBlockPool(uint64_t blockPoolId) override;
+ virtual Return<Status> start() override;
+ virtual Return<Status> stop() override;
+ virtual Return<Status> reset() override;
+ virtual Return<Status> release() override;
+ virtual Return<sp<IComponentInterface>> getInterface() override;
+ virtual Return<sp<IInputSink>> asInputSink() override;
+ virtual Return<void> configureVideoTunnel(
+ uint32_t avSyncHwId, configureVideoTunnel_cb _hidl_cb) override;
+
+ // Returns a C2Component associated to the given sink if the sink is indeed
+ // a local component. Returns nullptr otherwise.
+ //
+ // This function is used by InputSurface::connect().
+ static std::shared_ptr<C2Component> findLocalComponent(
+ const sp<IInputSink>& sink);
+
+protected:
+ c2_status_t mInit;
+ std::shared_ptr<C2Component> mComponent;
+ sp<ComponentInterface> mInterface;
+ sp<IComponentListener> mListener;
+ sp<ComponentStore> mStore;
+ ::android::hardware::media::c2::V1_1::utils::DefaultBufferPoolSender
+ mBufferPoolSender;
+
+ struct Sink;
+ std::mutex mSinkMutex;
+ sp<Sink> mSink;
+
+ std::mutex mBlockPoolsMutex;
+ // This map keeps C2BlockPool objects that are created by createBlockPool()
+ // alive. These C2BlockPool objects can be deleted by calling
+ // destroyBlockPool(), reset() or release(), or by destroying the component.
+ std::map<uint64_t, std::shared_ptr<C2BlockPool>> mBlockPools;
+
+ void initListener(const sp<Component>& self);
+
+ virtual ~Component() override;
+
+ friend struct ComponentStore;
+
+ struct Listener;
+};
+
+} // namespace utils
+} // namespace V1_1
+} // namespace c2
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // CODEC2_HIDL_V1_1_UTILS_COMPONENT_H
diff --git a/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/ComponentInterface.h b/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/ComponentInterface.h
new file mode 100644
index 0000000..723c5bd
--- /dev/null
+++ b/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/ComponentInterface.h
@@ -0,0 +1,39 @@
+/*
+ * 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 CODEC2_HIDL_V1_1_UTILS_COMPONENT_INTERFACE_H
+#define CODEC2_HIDL_V1_1_UTILS_COMPONENT_INTERFACE_H
+
+#include <codec2/hidl/1.0/ComponentInterface.h>
+#include <codec2/hidl/1.1/types.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_1 {
+namespace utils {
+
+using ::android::hardware::media::c2::V1_0::utils::ComponentInterface;
+
+} // namespace utils
+} // namespace V1_1
+} // namespace c2
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // CODEC2_HIDL_V1_1_UTILS_COMPONENT_INTERFACE_H
diff --git a/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/ComponentStore.h b/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/ComponentStore.h
new file mode 100644
index 0000000..1f04391
--- /dev/null
+++ b/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/ComponentStore.h
@@ -0,0 +1,166 @@
+/*
+ * 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 CODEC2_HIDL_V1_1_UTILS_COMPONENT_STORE_H
+#define CODEC2_HIDL_V1_1_UTILS_COMPONENT_STORE_H
+
+#include <codec2/hidl/1.1/Component.h>
+#include <codec2/hidl/1.1/ComponentInterface.h>
+#include <codec2/hidl/1.1/Configurable.h>
+#include <codec2/hidl/1.1/types.h>
+
+#include <android/hardware/media/bufferpool/2.0/IClientManager.h>
+#include <android/hardware/media/c2/1.1/IComponentStore.h>
+#include <hidl/Status.h>
+
+#include <C2Component.h>
+#include <C2Param.h>
+#include <C2.h>
+
+#include <chrono>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <set>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_1 {
+namespace utils {
+
+using ::android::hardware::media::bufferpool::V2_0::IClientManager;
+
+using ::android::hardware::hidl_handle;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct ComponentStore : public IComponentStore {
+ ComponentStore(const std::shared_ptr<C2ComponentStore>& store);
+ virtual ~ComponentStore();
+
+ /**
+ * Returns the status of the construction of this object.
+ */
+ c2_status_t status() const;
+
+ /**
+ * This function is called by CachedConfigurable::init() to validate
+ * supported parameters.
+ */
+ c2_status_t validateSupportedParams(
+ const std::vector<std::shared_ptr<C2ParamDescriptor>>& params);
+
+ /**
+ * Returns the store's ParameterCache. This is used for validation by
+ * Configurable::init().
+ */
+ std::shared_ptr<ParameterCache> getParameterCache() const;
+
+ // Methods from ::android::hardware::media::c2::V1_0::IComponentStore.
+ virtual Return<void> createComponent(
+ const hidl_string& name,
+ const sp<IComponentListener>& listener,
+ const sp<IClientManager>& pool,
+ createComponent_cb _hidl_cb) override;
+ virtual Return<void> createInterface(
+ const hidl_string& name,
+ createInterface_cb _hidl_cb) override;
+ virtual Return<void> listComponents(listComponents_cb _hidl_cb) override;
+ virtual Return<void> createInputSurface(
+ createInputSurface_cb _hidl_cb) override;
+ virtual Return<void> getStructDescriptors(
+ const hidl_vec<uint32_t>& indices,
+ getStructDescriptors_cb _hidl_cb) override;
+ virtual Return<sp<IClientManager>> getPoolClientManager() override;
+ virtual Return<Status> copyBuffer(
+ const Buffer& src,
+ const Buffer& dst) override;
+ virtual Return<sp<IConfigurable>> getConfigurable() override;
+
+ // Methods from ::android::hardware::media::c2::V1_1::IComponentStore.
+ virtual Return<void> createComponent_1_1(
+ const hidl_string& name,
+ const sp<IComponentListener>& listener,
+ const sp<IClientManager>& pool,
+ createComponent_1_1_cb _hidl_cb) override;
+
+ /**
+ * Dumps information when lshal is called.
+ */
+ virtual Return<void> debug(
+ const hidl_handle& handle,
+ const hidl_vec<hidl_string>& args) override;
+
+protected:
+ sp<CachedConfigurable> mConfigurable;
+ struct StoreParameterCache;
+ std::shared_ptr<StoreParameterCache> mParameterCache;
+
+ // Does bookkeeping for an interface that has been loaded.
+ void onInterfaceLoaded(const std::shared_ptr<C2ComponentInterface> &intf);
+
+ c2_status_t mInit;
+ std::shared_ptr<C2ComponentStore> mStore;
+ std::shared_ptr<C2ParamReflector> mParamReflector;
+
+ std::map<C2Param::CoreIndex, std::shared_ptr<C2StructDescriptor>> mStructDescriptors;
+ std::set<C2Param::CoreIndex> mUnsupportedStructDescriptors;
+ std::set<C2String> mLoadedInterfaces;
+ mutable std::mutex mStructDescriptorsMutex;
+
+ // ComponentStore keeps track of live Components.
+
+ struct ComponentStatus {
+ std::shared_ptr<C2Component> c2Component;
+ std::chrono::system_clock::time_point birthTime;
+ };
+
+ mutable std::mutex mComponentRosterMutex;
+ std::map<Component*, ComponentStatus> mComponentRoster;
+
+ // Called whenever Component is created.
+ void reportComponentBirth(Component* component);
+ // Called only from the destructor of Component.
+ void reportComponentDeath(Component* component);
+
+ friend Component;
+
+ // Helper functions for dumping.
+
+ std::ostream& dump(
+ std::ostream& out,
+ const std::shared_ptr<const C2Component::Traits>& comp);
+
+ std::ostream& dump(
+ std::ostream& out,
+ ComponentStatus& compStatus);
+
+};
+
+} // namespace utils
+} // namespace V1_1
+} // namespace c2
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // CODEC2_HIDL_V1_1_UTILS_COMPONENT_STORE_H
diff --git a/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/Configurable.h b/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/Configurable.h
new file mode 100644
index 0000000..fd9091b
--- /dev/null
+++ b/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/Configurable.h
@@ -0,0 +1,41 @@
+/*
+ * 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 CODEC2_HIDL_V1_1_UTILS_CONFIGURABLE_H
+#define CODEC2_HIDL_V1_1_UTILS_CONFIGURABLE_H
+
+#include <codec2/hidl/1.0/Configurable.h>
+#include <codec2/hidl/1.1/types.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_1 {
+namespace utils {
+
+using ::android::hardware::media::c2::V1_0::utils::ConfigurableC2Intf;
+using ::android::hardware::media::c2::V1_0::utils::ParameterCache;
+using ::android::hardware::media::c2::V1_0::utils::CachedConfigurable;
+
+} // namespace utils
+} // namespace V1_1
+} // namespace c2
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // CODEC2_HIDL_V1_1_UTILS_CONFIGURABLE_H
diff --git a/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/InputBufferManager.h b/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/InputBufferManager.h
new file mode 100644
index 0000000..8e7a91b
--- /dev/null
+++ b/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/InputBufferManager.h
@@ -0,0 +1,39 @@
+/*
+ * 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 CODEC2_HIDL_V1_1_UTILS_INPUT_BUFFER_MANAGER_H
+#define CODEC2_HIDL_V1_1_UTILS_INPUT_BUFFER_MANAGER_H
+
+#include <codec2/hidl/1.0/InputBufferManager.h>
+#include <codec2/hidl/1.1/types.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_1 {
+namespace utils {
+
+using ::android::hardware::media::c2::V1_0::utils::InputBufferManager;
+
+} // namespace utils
+} // namespace V1_1
+} // namespace c2
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // CODEC2_HIDL_V1_1_UTILS_INPUT_BUFFER_MANAGER_H
diff --git a/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/InputSurface.h b/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/InputSurface.h
new file mode 100644
index 0000000..59223b7
--- /dev/null
+++ b/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/InputSurface.h
@@ -0,0 +1,39 @@
+/*
+ * 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 CODEC2_HIDL_V1_1_UTILS_INPUT_SURFACE_H
+#define CODEC2_HIDL_V1_1_UTILS_INPUT_SURFACE_H
+
+#include <codec2/hidl/1.0/InputSurface.h>
+#include <codec2/hidl/1.1/types.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_1 {
+namespace utils {
+
+using ::android::hardware::media::c2::V1_0::utils::InputSurface;
+
+} // namespace utils
+} // namespace V1_1
+} // namespace c2
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // CODEC2_HIDL_V1_1_UTILS_INPUT_SURFACE_H
diff --git a/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/InputSurfaceConnection.h b/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/InputSurfaceConnection.h
new file mode 100644
index 0000000..7f695ef
--- /dev/null
+++ b/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/InputSurfaceConnection.h
@@ -0,0 +1,39 @@
+/*
+ * 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 CODEC2_HIDL_V1_1_UTILS_INPUT_SURFACE_CONNECTION_H
+#define CODEC2_HIDL_V1_1_UTILS_INPUT_SURFACE_CONNECTION_H
+
+#include <codec2/hidl/1.0/InputSurfaceConnection.h>
+#include <codec2/hidl/1.1/types.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_1 {
+namespace utils {
+
+using ::android::hardware::media::c2::V1_0::utils::InputSurfaceConnection;
+
+} // namespace utils
+} // namespace V1_1
+} // namespace c2
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // CODEC2_HIDL_V1_1_UTILS_INPUT_SURFACE_CONNECTION_H
diff --git a/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/OutputBufferQueue.h b/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/OutputBufferQueue.h
new file mode 100644
index 0000000..f77852d
--- /dev/null
+++ b/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/OutputBufferQueue.h
@@ -0,0 +1,39 @@
+/*
+ * 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 CODEC2_HIDL_V1_1_UTILS_OUTPUT_BUFFER_QUEUE
+#define CODEC2_HIDL_V1_1_UTILS_OUTPUT_BUFFER_QUEUE
+
+#include <codec2/hidl/1.0/OutputBufferQueue.h>
+#include <codec2/hidl/1.1/types.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_1 {
+namespace utils {
+
+using ::android::hardware::media::c2::V1_0::utils::OutputBufferQueue;
+
+} // namespace utils
+} // namespace V1_1
+} // namespace c2
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // CODEC2_HIDL_V1_1_UTILS_OUTPUT_BUFFER_QUEUE
diff --git a/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/types.h b/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/types.h
new file mode 100644
index 0000000..d0ba93d
--- /dev/null
+++ b/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/types.h
@@ -0,0 +1,102 @@
+/*
+ * 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 CODEC2_HIDL_V1_1_UTILS_TYPES_H
+#define CODEC2_HIDL_V1_1_UTILS_TYPES_H
+
+#include <android/hardware/media/c2/1.1/IComponent.h>
+#include <android/hardware/media/c2/1.0/IComponentInterface.h>
+#include <android/hardware/media/c2/1.0/IComponentListener.h>
+#include <android/hardware/media/c2/1.1/IComponentStore.h>
+#include <android/hardware/media/c2/1.0/IConfigurable.h>
+#include <android/hardware/media/c2/1.0/IInputSink.h>
+#include <android/hardware/media/c2/1.0/IInputSurface.h>
+#include <android/hardware/media/c2/1.0/IInputSurfaceConnection.h>
+
+#include <codec2/hidl/1.0/types.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_1 {
+
+using ::android::hardware::media::c2::V1_0::BaseBlock;
+using ::android::hardware::media::c2::V1_0::Block;
+using ::android::hardware::media::c2::V1_0::Buffer;
+using ::android::hardware::media::c2::V1_0::FieldDescriptor;
+using ::android::hardware::media::c2::V1_0::FieldId;
+using ::android::hardware::media::c2::V1_0::FieldSupportedValues;
+using ::android::hardware::media::c2::V1_0::FieldSupportedValuesQuery;
+using ::android::hardware::media::c2::V1_0::FieldSupportedValuesQueryResult;
+using ::android::hardware::media::c2::V1_0::FrameData;
+using ::android::hardware::media::c2::V1_0::InfoBuffer;
+using ::android::hardware::media::c2::V1_0::ParamDescriptor;
+using ::android::hardware::media::c2::V1_0::ParamField;
+using ::android::hardware::media::c2::V1_0::ParamFieldValues;
+using ::android::hardware::media::c2::V1_0::ParamIndex;
+using ::android::hardware::media::c2::V1_0::Params;
+using ::android::hardware::media::c2::V1_0::PrimitiveValue;
+using ::android::hardware::media::c2::V1_0::SettingResult;
+using ::android::hardware::media::c2::V1_0::Status;
+using ::android::hardware::media::c2::V1_0::StructDescriptor;
+using ::android::hardware::media::c2::V1_0::ValueRange;
+using ::android::hardware::media::c2::V1_0::Work;
+using ::android::hardware::media::c2::V1_0::WorkBundle;
+using ::android::hardware::media::c2::V1_0::WorkOrdinal;
+using ::android::hardware::media::c2::V1_0::Worklet;
+
+using ::android::hardware::media::c2::V1_0::IComponentInterface;
+using ::android::hardware::media::c2::V1_0::IComponentListener;
+using ::android::hardware::media::c2::V1_0::IConfigurable;
+using ::android::hardware::media::c2::V1_0::IInputSink;
+using ::android::hardware::media::c2::V1_0::IInputSurface;
+using ::android::hardware::media::c2::V1_0::IInputSurfaceConnection;
+
+namespace utils {
+
+using ::android::hardware::media::c2::V1_0::utils::toC2Status;
+
+using ::android::hardware::media::c2::V1_0::utils::C2Hidl_Range;
+using ::android::hardware::media::c2::V1_0::utils::C2Hidl_RangeInfo;
+using ::android::hardware::media::c2::V1_0::utils::C2Hidl_Rect;
+using ::android::hardware::media::c2::V1_0::utils::C2Hidl_RectInfo;
+
+using ::android::hardware::media::c2::V1_0::utils::objcpy;
+using ::android::hardware::media::c2::V1_0::utils::parseParamsBlob;
+using ::android::hardware::media::c2::V1_0::utils::createParamsBlob;
+using ::android::hardware::media::c2::V1_0::utils::copyParamsFromBlob;
+using ::android::hardware::media::c2::V1_0::utils::updateParamsFromBlob;
+
+using ::android::hardware::media::c2::V1_0::utils::BufferPoolSender;
+using ::android::hardware::media::c2::V1_0::utils::DefaultBufferPoolSender;
+
+using ::android::hardware::media::c2::V1_0::utils::beginTransferBufferQueueBlock;
+using ::android::hardware::media::c2::V1_0::utils::beginTransferBufferQueueBlocks;
+using ::android::hardware::media::c2::V1_0::utils::endTransferBufferQueueBlock;
+using ::android::hardware::media::c2::V1_0::utils::endTransferBufferQueueBlocks;
+using ::android::hardware::media::c2::V1_0::utils::displayBufferQueueBlock;
+
+using ::android::hardware::media::c2::V1_0::utils::operator<<;
+
+} // namespace utils
+} // namespace V1_1
+} // namespace c2
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // CODEC2_HIDL_V1_1_UTILS_TYPES_H
diff --git a/media/codec2/hidl/1.1/utils/types.cpp b/media/codec2/hidl/1.1/utils/types.cpp
new file mode 100644
index 0000000..8c09023
--- /dev/null
+++ b/media/codec2/hidl/1.1/utils/types.cpp
@@ -0,0 +1,17 @@
+/*
+ * 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.
+ */
+
+#include <codec2/hidl/1.1/types.h>
diff --git a/media/codec2/hidl/client/Android.bp b/media/codec2/hidl/client/Android.bp
index 89c1c4a..3c37990 100644
--- a/media/codec2/hidl/client/Android.bp
+++ b/media/codec2/hidl/client/Android.bp
@@ -9,10 +9,12 @@
"android.hardware.graphics.bufferqueue@1.0",
"android.hardware.media.bufferpool@2.0",
"android.hardware.media.c2@1.0",
+ "android.hardware.media.c2@1.1",
"libbase",
"libbinder",
"libcodec2",
"libcodec2_hidl_client@1.0",
+ "libcodec2_hidl_client@1.1",
"libcodec2_vndk",
"libcutils",
"libgui",
@@ -28,8 +30,11 @@
],
export_shared_lib_headers: [
+ "android.hardware.media.c2@1.0",
+ "android.hardware.media.c2@1.1",
"libcodec2",
"libcodec2_hidl_client@1.0",
+ "libcodec2_hidl_client@1.1",
"libcodec2_vndk",
],
diff --git a/media/codec2/hidl/client/client.cpp b/media/codec2/hidl/client/client.cpp
index c747190..199a99c 100644
--- a/media/codec2/hidl/client/client.cpp
+++ b/media/codec2/hidl/client/client.cpp
@@ -19,6 +19,29 @@
#include <android-base/logging.h>
#include <codec2/hidl/client.h>
+#include <C2Debug.h>
+#include <C2BufferPriv.h>
+#include <C2PlatformSupport.h>
+
+#include <android/hardware/media/bufferpool/2.0/IClientManager.h>
+#include <android/hardware/media/c2/1.0/IComponent.h>
+#include <android/hardware/media/c2/1.0/IComponentInterface.h>
+#include <android/hardware/media/c2/1.0/IComponentListener.h>
+#include <android/hardware/media/c2/1.0/IComponentStore.h>
+#include <android/hardware/media/c2/1.0/IConfigurable.h>
+#include <android/hidl/manager/1.2/IServiceManager.h>
+
+#include <android-base/properties.h>
+#include <bufferpool/ClientManager.h>
+#include <codec2/hidl/1.0/OutputBufferQueue.h>
+#include <codec2/hidl/1.0/types.h>
+#include <codec2/hidl/1.1/OutputBufferQueue.h>
+#include <codec2/hidl/1.1/types.h>
+
+#include <cutils/native_handle.h>
+#include <gui/bufferqueue/2.0/B2HGraphicBufferProducer.h>
+#include <gui/bufferqueue/2.0/H2BGraphicBufferProducer.h>
+#include <hidl/HidlSupport.h>
#include <deque>
#include <iterator>
@@ -30,25 +53,6 @@
#include <type_traits>
#include <vector>
-#include <android-base/properties.h>
-#include <bufferpool/ClientManager.h>
-#include <cutils/native_handle.h>
-#include <gui/bufferqueue/2.0/B2HGraphicBufferProducer.h>
-#include <gui/bufferqueue/2.0/H2BGraphicBufferProducer.h>
-#include <hidl/HidlSupport.h>
-
-#include <android/hardware/media/bufferpool/2.0/IClientManager.h>
-#include <android/hardware/media/c2/1.0/IComponent.h>
-#include <android/hardware/media/c2/1.0/IComponentInterface.h>
-#include <android/hardware/media/c2/1.0/IComponentListener.h>
-#include <android/hardware/media/c2/1.0/IComponentStore.h>
-#include <android/hardware/media/c2/1.0/IConfigurable.h>
-#include <android/hidl/manager/1.2/IServiceManager.h>
-
-#include <C2Debug.h>
-#include <C2BufferPriv.h>
-#include <C2PlatformSupport.h>
-
namespace android {
using ::android::hardware::hidl_vec;
@@ -56,8 +60,8 @@
using ::android::hardware::Return;
using ::android::hardware::Void;
-using namespace ::android::hardware::media::c2::V1_0;
-using namespace ::android::hardware::media::c2::V1_0::utils;
+using namespace ::android::hardware::media::c2::V1_1;
+using namespace ::android::hardware::media::c2::V1_1::utils;
using namespace ::android::hardware::media::bufferpool::V2_0;
using namespace ::android::hardware::media::bufferpool::V2_0::implementation;
@@ -514,8 +518,24 @@
};
+// Codec2Client::Component::BufferPoolSender
+struct Codec2Client::Component::BufferPoolSender :
+ hardware::media::c2::V1_1::utils::DefaultBufferPoolSender {
+ BufferPoolSender()
+ : hardware::media::c2::V1_1::utils::DefaultBufferPoolSender() {
+ }
+};
+
+// Codec2Client::Component::OutputBufferQueue
+struct Codec2Client::Component::OutputBufferQueue :
+ hardware::media::c2::V1_1::utils::OutputBufferQueue {
+ OutputBufferQueue()
+ : hardware::media::c2::V1_1::utils::OutputBufferQueue() {
+ }
+};
+
// Codec2Client
-Codec2Client::Codec2Client(const sp<IComponentStore>& base,
+Codec2Client::Codec2Client(sp<Base> const& base,
size_t serviceIndex)
: Configurable{
[base]() -> sp<IConfigurable> {
@@ -526,7 +546,8 @@
nullptr;
}()
},
- mBase{base},
+ mBase1_0{base},
+ mBase1_1{Base1_1::castFrom(base)},
mServiceIndex{serviceIndex} {
Return<sp<IClientManager>> transResult = base->getPoolClientManager();
if (!transResult.isOk()) {
@@ -537,7 +558,15 @@
}
sp<Codec2Client::Base> const& Codec2Client::getBase() const {
- return mBase;
+ return mBase1_0;
+}
+
+sp<Codec2Client::Base1_0> const& Codec2Client::getBase1_0() const {
+ return mBase1_0;
+}
+
+sp<Codec2Client::Base1_1> const& Codec2Client::getBase1_1() const {
+ return mBase1_1;
}
std::string const& Codec2Client::getServiceName() const {
@@ -552,7 +581,8 @@
c2_status_t status;
sp<Component::HidlListener> hidlListener = new Component::HidlListener{};
hidlListener->base = listener;
- Return<void> transStatus = mBase->createComponent(
+ Return<void> transStatus = mBase1_1 ?
+ mBase1_1->createComponent_1_1(
name,
hidlListener,
ClientManager::getInstance(),
@@ -565,6 +595,20 @@
}
*component = std::make_shared<Codec2Client::Component>(c);
hidlListener->component = *component;
+ }) :
+ mBase1_0->createComponent(
+ name,
+ hidlListener,
+ ClientManager::getInstance(),
+ [&status, component, hidlListener](
+ Status s,
+ const sp<hardware::media::c2::V1_0::IComponent>& c) {
+ status = static_cast<c2_status_t>(s);
+ if (status != C2_OK) {
+ return;
+ }
+ *component = std::make_shared<Codec2Client::Component>(c);
+ hidlListener->component = *component;
});
if (!transStatus.isOk()) {
LOG(ERROR) << "createComponent(" << name.c_str()
@@ -587,7 +631,7 @@
<< status << ".";
}
- (*component)->mBufferPoolSender.setReceiver(mHostPoolManager);
+ (*component)->mBufferPoolSender->setReceiver(mHostPoolManager);
return status;
}
@@ -595,7 +639,7 @@
const C2String& name,
std::shared_ptr<Codec2Client::Interface>* const interface) {
c2_status_t status;
- Return<void> transStatus = mBase->createInterface(
+ Return<void> transStatus = mBase1_0->createInterface(
name,
[&status, interface](
Status s,
@@ -622,7 +666,7 @@
c2_status_t Codec2Client::createInputSurface(
std::shared_ptr<InputSurface>* const inputSurface) {
c2_status_t status;
- Return<void> transStatus = mBase->createInputSurface(
+ Return<void> transStatus = mBase1_0->createInputSurface(
[&status, inputSurface](
Status s,
const sp<IInputSurface>& i) {
@@ -650,7 +694,7 @@
bool* success) const {
std::vector<C2Component::Traits> traits;
std::string const& serviceName = getServiceName();
- Return<void> transStatus = mBase->listComponents(
+ Return<void> transStatus = mBase1_0->listComponents(
[&traits, &serviceName](Status s,
const hidl_vec<IComponentStore::ComponentTraits>& t) {
if (s != Status::OK) {
@@ -734,7 +778,7 @@
sp<Base> mBase;
};
- return std::make_shared<SimpleParamReflector>(mBase);
+ return std::make_shared<SimpleParamReflector>(mBase1_0);
};
std::vector<std::string> const& Codec2Client::GetServiceNames() {
@@ -1053,8 +1097,32 @@
nullptr;
}()
},
- mBase{base},
- mBufferPoolSender{nullptr} {
+ mBase1_0{base},
+ mBase1_1{Base1_1::castFrom(base)},
+ mBufferPoolSender{std::make_unique<BufferPoolSender>()},
+ mOutputBufferQueue{std::make_unique<OutputBufferQueue>()} {
+}
+
+Codec2Client::Component::Component(const sp<Base1_1>& base)
+ : Configurable{
+ [base]() -> sp<IConfigurable> {
+ Return<sp<IComponentInterface>> transResult1 =
+ base->getInterface();
+ if (!transResult1.isOk()) {
+ return nullptr;
+ }
+ Return<sp<IConfigurable>> transResult2 =
+ static_cast<sp<IComponentInterface>>(transResult1)->
+ getConfigurable();
+ return transResult2.isOk() ?
+ static_cast<sp<IConfigurable>>(transResult2) :
+ nullptr;
+ }()
+ },
+ mBase1_0{base},
+ mBase1_1{base},
+ mBufferPoolSender{std::make_unique<BufferPoolSender>()},
+ mOutputBufferQueue{std::make_unique<OutputBufferQueue>()} {
}
Codec2Client::Component::~Component() {
@@ -1065,7 +1133,7 @@
C2BlockPool::local_id_t* blockPoolId,
std::shared_ptr<Codec2Client::Configurable>* configurable) {
c2_status_t status;
- Return<void> transStatus = mBase->createBlockPool(
+ Return<void> transStatus = mBase1_0->createBlockPool(
static_cast<uint32_t>(id),
[&status, blockPoolId, configurable](
Status s,
@@ -1090,7 +1158,7 @@
c2_status_t Codec2Client::Component::destroyBlockPool(
C2BlockPool::local_id_t localId) {
- Return<Status> transResult = mBase->destroyBlockPool(
+ Return<Status> transResult = mBase1_0->destroyBlockPool(
static_cast<uint64_t>(localId));
if (!transResult.isOk()) {
LOG(ERROR) << "destroyBlockPool -- transaction failed.";
@@ -1102,17 +1170,17 @@
void Codec2Client::Component::handleOnWorkDone(
const std::list<std::unique_ptr<C2Work>> &workItems) {
// Output bufferqueue-based blocks' lifetime management
- mOutputBufferQueue.holdBufferQueueBlocks(workItems);
+ mOutputBufferQueue->holdBufferQueueBlocks(workItems);
}
c2_status_t Codec2Client::Component::queue(
std::list<std::unique_ptr<C2Work>>* const items) {
WorkBundle workBundle;
- if (!objcpy(&workBundle, *items, &mBufferPoolSender)) {
+ if (!objcpy(&workBundle, *items, mBufferPoolSender.get())) {
LOG(ERROR) << "queue -- bad input.";
return C2_TRANSACTION_FAILED;
}
- Return<Status> transStatus = mBase->queue(workBundle);
+ Return<Status> transStatus = mBase1_0->queue(workBundle);
if (!transStatus.isOk()) {
LOG(ERROR) << "queue -- transaction failed.";
return C2_TRANSACTION_FAILED;
@@ -1130,7 +1198,7 @@
std::list<std::unique_ptr<C2Work>>* const flushedWork) {
(void)mode; // Flush mode isn't supported in HIDL yet.
c2_status_t status;
- Return<void> transStatus = mBase->flush(
+ Return<void> transStatus = mBase1_0->flush(
[&status, flushedWork](
Status s, const WorkBundle& wb) {
status = static_cast<c2_status_t>(s);
@@ -1165,13 +1233,13 @@
}
// Output bufferqueue-based blocks' lifetime management
- mOutputBufferQueue.holdBufferQueueBlocks(*flushedWork);
+ mOutputBufferQueue->holdBufferQueueBlocks(*flushedWork);
return status;
}
c2_status_t Codec2Client::Component::drain(C2Component::drain_mode_t mode) {
- Return<Status> transStatus = mBase->drain(
+ Return<Status> transStatus = mBase1_0->drain(
mode == C2Component::DRAIN_COMPONENT_WITH_EOS);
if (!transStatus.isOk()) {
LOG(ERROR) << "drain -- transaction failed.";
@@ -1186,7 +1254,7 @@
}
c2_status_t Codec2Client::Component::start() {
- Return<Status> transStatus = mBase->start();
+ Return<Status> transStatus = mBase1_0->start();
if (!transStatus.isOk()) {
LOG(ERROR) << "start -- transaction failed.";
return C2_TRANSACTION_FAILED;
@@ -1200,7 +1268,7 @@
}
c2_status_t Codec2Client::Component::stop() {
- Return<Status> transStatus = mBase->stop();
+ Return<Status> transStatus = mBase1_0->stop();
if (!transStatus.isOk()) {
LOG(ERROR) << "stop -- transaction failed.";
return C2_TRANSACTION_FAILED;
@@ -1214,7 +1282,7 @@
}
c2_status_t Codec2Client::Component::reset() {
- Return<Status> transStatus = mBase->reset();
+ Return<Status> transStatus = mBase1_0->reset();
if (!transStatus.isOk()) {
LOG(ERROR) << "reset -- transaction failed.";
return C2_TRANSACTION_FAILED;
@@ -1228,7 +1296,7 @@
}
c2_status_t Codec2Client::Component::release() {
- Return<Status> transStatus = mBase->release();
+ Return<Status> transStatus = mBase1_0->release();
if (!transStatus.isOk()) {
LOG(ERROR) << "release -- transaction failed.";
return C2_TRANSACTION_FAILED;
@@ -1241,6 +1309,29 @@
return status;
}
+c2_status_t Codec2Client::Component::configureVideoTunnel(
+ uint32_t avSyncHwId,
+ native_handle_t** sidebandHandle) {
+ *sidebandHandle = nullptr;
+ if (!mBase1_1) {
+ return C2_OMITTED;
+ }
+ c2_status_t status{};
+ Return<void> transStatus = mBase1_1->configureVideoTunnel(avSyncHwId,
+ [&status, sidebandHandle](
+ Status s, hardware::hidl_handle const& h) {
+ status = static_cast<c2_status_t>(s);
+ if (h.getNativeHandle()) {
+ *sidebandHandle = native_handle_clone(h.getNativeHandle());
+ }
+ });
+ if (!transStatus.isOk()) {
+ LOG(ERROR) << "configureVideoTunnel -- transaction failed.";
+ return C2_TRANSACTION_FAILED;
+ }
+ return status;
+}
+
c2_status_t Codec2Client::Component::setOutputSurface(
C2BlockPool::local_id_t blockPoolId,
const sp<IGraphicBufferProducer>& surface,
@@ -1256,18 +1347,18 @@
}
if (!surface) {
- mOutputBufferQueue.configure(nullIgbp, generation, 0);
+ mOutputBufferQueue->configure(nullIgbp, generation, 0);
} else if (surface->getUniqueId(&bqId) != OK) {
LOG(ERROR) << "setOutputSurface -- "
"cannot obtain bufferqueue id.";
bqId = 0;
- mOutputBufferQueue.configure(nullIgbp, generation, 0);
+ mOutputBufferQueue->configure(nullIgbp, generation, 0);
} else {
- mOutputBufferQueue.configure(surface, generation, bqId);
+ mOutputBufferQueue->configure(surface, generation, bqId);
}
ALOGD("generation remote change %u", generation);
- Return<Status> transStatus = mBase->setOutputSurface(
+ Return<Status> transStatus = mBase1_0->setOutputSurface(
static_cast<uint64_t>(blockPoolId),
bqId == 0 ? nullHgbp : igbp);
if (!transStatus.isOk()) {
@@ -1286,14 +1377,14 @@
const C2ConstGraphicBlock& block,
const QueueBufferInput& input,
QueueBufferOutput* output) {
- return mOutputBufferQueue.outputBuffer(block, input, output);
+ return mOutputBufferQueue->outputBuffer(block, input, output);
}
c2_status_t Codec2Client::Component::connectToInputSurface(
const std::shared_ptr<InputSurface>& inputSurface,
std::shared_ptr<InputSurfaceConnection>* connection) {
c2_status_t status;
- Return<void> transStatus = mBase->connectToInputSurface(
+ Return<void> transStatus = mBase1_0->connectToInputSurface(
inputSurface->mBase,
[&status, connection](
Status s, const sp<IInputSurfaceConnection>& c) {
@@ -1317,7 +1408,7 @@
const sp<HGraphicBufferSource>& source,
std::shared_ptr<InputSurfaceConnection>* connection) {
c2_status_t status;
- Return<void> transStatus = mBase->connectToOmxInputSurface(
+ Return<void> transStatus = mBase1_0->connectToOmxInputSurface(
producer, source,
[&status, connection](
Status s, const sp<IInputSurfaceConnection>& c) {
@@ -1337,7 +1428,7 @@
}
c2_status_t Codec2Client::Component::disconnectFromInputSurface() {
- Return<Status> transStatus = mBase->disconnectFromInputSurface();
+ Return<Status> transStatus = mBase1_0->disconnectFromInputSurface();
if (!transStatus.isOk()) {
LOG(ERROR) << "disconnectToInputSurface -- transaction failed.";
return C2_TRANSACTION_FAILED;
@@ -1376,7 +1467,7 @@
deathRecipient->component = component;
component->mDeathRecipient = deathRecipient;
- Return<bool> transResult = component->mBase->linkToDeath(
+ Return<bool> transResult = component->mBase1_0->linkToDeath(
component->mDeathRecipient, 0);
if (!transResult.isOk()) {
LOG(ERROR) << "setDeathListener -- linkToDeath() transaction failed.";
diff --git a/media/codec2/hidl/client/include/codec2/hidl/client.h b/media/codec2/hidl/client/include/codec2/hidl/client.h
index c37407f..649dffd 100644
--- a/media/codec2/hidl/client/include/codec2/hidl/client.h
+++ b/media/codec2/hidl/client/include/codec2/hidl/client.h
@@ -17,14 +17,13 @@
#ifndef CODEC2_HIDL_CLIENT_H
#define CODEC2_HIDL_CLIENT_H
-#include <gui/IGraphicBufferProducer.h>
-#include <codec2/hidl/1.0/ClientBlockHelper.h>
#include <C2PlatformSupport.h>
#include <C2Component.h>
#include <C2Buffer.h>
#include <C2Param.h>
#include <C2.h>
+#include <gui/IGraphicBufferProducer.h>
#include <hidl/HidlSupport.h>
#include <utils/StrongPointer.h>
@@ -74,6 +73,11 @@
struct IInputSurfaceConnection;
} // namespace android::hardware::media::c2::V1_0
+namespace android::hardware::media::c2::V1_1 {
+struct IComponent;
+struct IComponentStore;
+} // namespace android::hardware::media::c2::V1_1
+
namespace android::hardware::media::bufferpool::V2_0 {
struct IClientManager;
} // namespace android::hardware::media::bufferpool::V2_0
@@ -82,6 +86,10 @@
struct IGraphicBufferProducer;
} // android::hardware::graphics::bufferqueue::V1_0
+namespace android::hardware::graphics::bufferqueue::V2_0 {
+struct IGraphicBufferProducer;
+} // android::hardware::graphics::bufferqueue::V2_0
+
namespace android::hardware::media::omx::V1_0 {
struct IGraphicBufferSource;
} // namespace android::hardware::media::omx::V1_0
@@ -127,7 +135,9 @@
struct Codec2Client : public Codec2ConfigurableClient {
- typedef ::android::hardware::media::c2::V1_0::IComponentStore Base;
+ typedef ::android::hardware::media::c2::V1_0::IComponentStore Base1_0;
+ typedef ::android::hardware::media::c2::V1_1::IComponentStore Base1_1;
+ typedef Base1_0 Base;
struct Listener;
@@ -144,6 +154,8 @@
typedef Codec2Client Store;
sp<Base> const& getBase() const;
+ sp<Base1_0> const& getBase1_0() const;
+ sp<Base1_1> const& getBase1_1() const;
std::string const& getServiceName() const;
@@ -206,7 +218,8 @@
Codec2Client(sp<Base> const& base, size_t serviceIndex);
protected:
- sp<Base> mBase;
+ sp<Base1_0> mBase1_0;
+ sp<Base1_1> mBase1_1;
// Finds the first store where the predicate returns C2_OK and returns the
// last predicate result. The predicate will be tried on all stores. The
@@ -295,7 +308,9 @@
struct Codec2Client::Component : public Codec2Client::Configurable {
- typedef ::android::hardware::media::c2::V1_0::IComponent Base;
+ typedef ::android::hardware::media::c2::V1_0::IComponent Base1_0;
+ typedef ::android::hardware::media::c2::V1_1::IComponent Base1_1;
+ typedef Base1_0 Base;
c2_status_t createBlockPool(
C2Allocator::id_t id,
@@ -322,6 +337,17 @@
c2_status_t release();
+ /**
+ * Use tunneling.
+ *
+ * On success, @p sidebandHandle will be a newly allocated native handle.
+ * File descriptors in @p sidebandHandle must be closed and
+ * @p sidebandHandle itself must be deleted afterwards.
+ */
+ c2_status_t configureVideoTunnel(
+ uint32_t avSyncHwId,
+ native_handle_t** sidebandHandle);
+
typedef ::android::
IGraphicBufferProducer IGraphicBufferProducer;
typedef IGraphicBufferProducer::
@@ -378,17 +404,19 @@
// base cannot be null.
Component(const sp<Base>& base);
+ Component(const sp<Base1_1>& base);
~Component();
protected:
- sp<Base> mBase;
+ sp<Base1_0> mBase1_0;
+ sp<Base1_1> mBase1_1;
- ::android::hardware::media::c2::V1_0::utils::DefaultBufferPoolSender
- mBufferPoolSender;
+ struct BufferPoolSender;
+ std::unique_ptr<BufferPoolSender> mBufferPoolSender;
- ::android::hardware::media::c2::V1_0::utils::OutputBufferQueue
- mOutputBufferQueue;
+ struct OutputBufferQueue;
+ std::unique_ptr<OutputBufferQueue> mOutputBufferQueue;
static c2_status_t setDeathListener(
const std::shared_ptr<Component>& component,
diff --git a/media/codec2/hidl/services/Android.bp b/media/codec2/hidl/services/Android.bp
index 0403a1f..46bea2e 100644
--- a/media/codec2/hidl/services/Android.bp
+++ b/media/codec2/hidl/services/Android.bp
@@ -1,37 +1,82 @@
+/*
+ * 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.
+ */
+
+// This is an example of an empty Codec2.0 service.
+//
+// To use this, make a copy of this whole directory and rename modules
+// accordingly. The contents of "vendor.cpp" and files in the subdirectory
+// "seccomp_policy" may also need to be modified.
+
+// Binary file for the service.
+//
+// The init_rc file contains the absolute path to this binary on the device.
+// If the name of this module is modified, the content of the init_rc file has
+// to be modified accordingly.
+//
+// The seccomp_policy file name and its content can be modified, but note that
+// vendor.cpp also needs to be updated because it needs the absolute path to the
+// seccomp policy file on the device.
cc_binary {
- name: "android.hardware.media.c2@1.0-service",
- defaults: ["hidl_defaults"],
- soc_specific: true,
+ name: "android.hardware.media.c2@1.1-default-service",
+ vendor: true,
relative_install_path: "hw",
+
+ init_rc: ["android.hardware.media.c2@1.1-default-service.rc"],
+
+ defaults: ["libcodec2-hidl-defaults"],
srcs: [
"vendor.cpp",
],
- init_rc: ["android.hardware.media.c2@1.0-service.rc"],
-
+ // minijail is used to protect against unexpected system calls.
shared_libs: [
- "android.hardware.media.c2@1.0",
- "android.hardware.media.omx@1.0",
"libavservices_minijail_vendor",
"libbinder",
- "libcodec2_hidl@1.0",
- "libcodec2_vndk",
- "libhidlbase",
- "liblog",
- "libstagefright_omx",
- "libstagefright_xmlparser",
- "libutils",
],
+ required: ["android.hardware.media.c2@1.1-default-seccomp_policy"],
+}
+// seccomp policy file.
+//
+// This should be modified to suit the target device and architecture.
+//
+// Files in the "seccomp_policy" subdirectory are only provided as examples.
+// They may not work on some devices and/or architectures without modification.
+prebuilt_etc {
+ name: "android.hardware.media.c2@1.1-default-seccomp_policy",
+ vendor: true,
+ sub_dir: "seccomp_policy",
+
+ // If a specific architecture is targeted, multiple choices are not needed.
arch: {
arm: {
- required: ["codec2.vendor.base.policy"],
+ src: "seccomp_policy/android.hardware.media.c2@1.1-default-arm.policy",
+ },
+ arm64: {
+ src: "seccomp_policy/android.hardware.media.c2@1.1-default-arm64.policy",
},
x86: {
- required: ["codec2.vendor.base.policy"],
+ src: "seccomp_policy/android.hardware.media.c2@1.1-default-x86.policy",
+ },
+ x86_64: {
+ src: "seccomp_policy/android.hardware.media.c2@1.1-default-x86_64.policy",
},
},
- compile_multilib: "32",
+ // This may be removed.
+ required: ["crash_dump.policy"],
}
diff --git a/media/codec2/hidl/services/Android.mk b/media/codec2/hidl/services/Android.mk
deleted file mode 100644
index d9b28e7..0000000
--- a/media/codec2/hidl/services/Android.mk
+++ /dev/null
@@ -1,24 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-# vendor service seccomp policy
-ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH), x86 x86_64 arm arm64))
-include $(CLEAR_VARS)
-LOCAL_MODULE := codec2.vendor.base.policy
-LOCAL_VENDOR_MODULE := true
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc/seccomp_policy
-LOCAL_REQUIRED_MODULES := crash_dump.policy
-ifdef TARGET_2ND_ARCH
- ifneq ($(TARGET_TRANSLATE_2ND_ARCH),true)
- LOCAL_SRC_FILES := seccomp_policy/codec2.vendor.base-$(TARGET_2ND_ARCH).policy
- else
- LOCAL_SRC_FILES := seccomp_policy/codec2.vendor.base-$(TARGET_ARCH).policy
- endif
-else
- LOCAL_SRC_FILES := seccomp_policy/codec2.vendor.base-$(TARGET_ARCH).policy
-endif
-include $(BUILD_PREBUILT)
-endif
-
-include $(call all-makefiles-under, $(LOCAL_PATH))
-
diff --git a/media/codec2/hidl/services/android.hardware.media.c2@1.0-service.rc b/media/codec2/hidl/services/android.hardware.media.c2@1.0-service.rc
deleted file mode 100644
index 8806bd1..0000000
--- a/media/codec2/hidl/services/android.hardware.media.c2@1.0-service.rc
+++ /dev/null
@@ -1,7 +0,0 @@
-service android-hardware-media-c2-hal-1-0 /vendor/bin/hw/android.hardware.media.c2@1.0-service
- class hal
- user mediacodec
- group camera mediadrm drmrpc
- ioprio rt 4
- writepid /dev/cpuset/foreground/tasks
-
diff --git a/media/codec2/hidl/services/android.hardware.media.c2@1.1-default-service.rc b/media/codec2/hidl/services/android.hardware.media.c2@1.1-default-service.rc
new file mode 100644
index 0000000..44f2d8e
--- /dev/null
+++ b/media/codec2/hidl/services/android.hardware.media.c2@1.1-default-service.rc
@@ -0,0 +1,7 @@
+service android-hardware-media-c2-hal-1-1 /vendor/bin/hw/android.hardware.media.c2@1.1-default-service
+ class hal
+ user mediacodec
+ group camera mediadrm drmrpc
+ ioprio rt 4
+ writepid /dev/cpuset/foreground/tasks
+
diff --git a/media/codec2/hidl/services/seccomp_policy/codec2.software.base-arm.policy b/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-arm.policy
similarity index 70%
copy from media/codec2/hidl/services/seccomp_policy/codec2.software.base-arm.policy
copy to media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-arm.policy
index d5871d1..9042cd7 100644
--- a/media/codec2/hidl/services/seccomp_policy/codec2.software.base-arm.policy
+++ b/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-arm.policy
@@ -1,4 +1,4 @@
-# Copyright (C) 2018 The Android Open Source Project
+# 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.
@@ -12,21 +12,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Organized by frequency of systemcall - in descending order for
-# best performance.
futex: 1
+# ioctl calls are filtered via the selinux policy.
ioctl: 1
-write: 1
-prctl: 1
-clock_gettime: 1
-getpriority: 1
-read: 1
+sched_yield: 1
close: 1
-writev: 1
dup: 1
ppoll: 1
-mmap2: 1
-getrandom: 1
+mprotect: arg2 in ~PROT_EXEC || arg2 in ~PROT_WRITE
+mmap2: arg2 in ~PROT_EXEC || arg2 in ~PROT_WRITE
+memfd_create: 1
+ftruncate: 1
+ftruncate64: 1
# mremap: Ensure |flags| are (MREMAP_MAYMOVE | MREMAP_FIXED) TODO: Once minijail
# parser support for '<' is in this needs to be modified to also prevent
@@ -36,38 +33,54 @@
# for more details.
mremap: arg3 == 3
munmap: 1
-mprotect: 1
-madvise: 1
-openat: 1
+prctl: 1
+getuid32: 1
+writev: 1
sigaltstack: 1
clone: 1
-setpriority: 1
-getuid32: 1
-fstat64: 1
-fstatfs64: 1
-pread64: 1
-faccessat: 1
-readlinkat: 1
exit: 1
-rt_sigprocmask: 1
-set_tid_address: 1
-restart_syscall: 1
-exit_group: 1
-rt_sigreturn: 1
-pipe2: 1
-gettimeofday: 1
-sched_yield: 1
-nanosleep: 1
lseek: 1
+rt_sigprocmask: 1
+openat: 1
+open: 1
+fstat64: 1
+write: 1
+nanosleep: 1
+setpriority: 1
+set_tid_address: 1
+getdents64: 1
+readlinkat: 1
+readlink: 1
+read: 1
+pread64: 1
+fstatfs64: 1
+gettimeofday: 1
+faccessat: 1
_llseek: 1
-sched_get_priority_max: 1
-sched_get_priority_min: 1
-statfs64: 1
-sched_setscheduler: 1
fstatat64: 1
ugetrlimit: 1
-getdents64: 1
+exit_group: 1
+restart_syscall: 1
+rt_sigreturn: 1
getrandom: 1
+madvise: 1
-@include /system/etc/seccomp_policy/crash_dump.arm.policy
-
+# crash dump policy additions
+sigreturn: 1
+clock_gettime: 1
+futex: 1
+getpid: 1
+gettid: 1
+pipe2: 1
+recvmsg: 1
+process_vm_readv: 1
+tgkill: 1
+rt_sigaction: 1
+rt_tgsigqueueinfo: 1
+#prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41
+#mprotect: arg2 in 0x1|0x2
+#mmap2: arg2 in 0x1|0x2
+geteuid32: 1
+getgid32: 1
+getegid32: 1
+getgroups32: 1
diff --git a/media/codec2/hidl/services/seccomp_policy/codec2.software.base-arm.policy b/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-arm64.policy
similarity index 71%
rename from media/codec2/hidl/services/seccomp_policy/codec2.software.base-arm.policy
rename to media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-arm64.policy
index d5871d1..4faf8b2 100644
--- a/media/codec2/hidl/services/seccomp_policy/codec2.software.base-arm.policy
+++ b/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-arm64.policy
@@ -1,4 +1,4 @@
-# Copyright (C) 2018 The Android Open Source Project
+# 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.
@@ -12,21 +12,22 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Organized by frequency of systemcall - in descending order for
-# best performance.
futex: 1
+# ioctl calls are filtered via the selinux policy.
ioctl: 1
-write: 1
-prctl: 1
-clock_gettime: 1
-getpriority: 1
-read: 1
+sched_yield: 1
close: 1
-writev: 1
dup: 1
ppoll: 1
-mmap2: 1
-getrandom: 1
+mprotect: arg2 in ~PROT_EXEC || arg2 in ~PROT_WRITE
+mmap: arg2 in ~PROT_EXEC || arg2 in ~PROT_WRITE
+getuid: 1
+getrlimit: 1
+fstat: 1
+newfstatat: 1
+fstatfs: 1
+memfd_create: 1
+ftruncate: 1
# mremap: Ensure |flags| are (MREMAP_MAYMOVE | MREMAP_FIXED) TODO: Once minijail
# parser support for '<' is in this needs to be modified to also prevent
@@ -36,38 +37,45 @@
# for more details.
mremap: arg3 == 3
munmap: 1
-mprotect: 1
-madvise: 1
-openat: 1
+prctl: 1
+writev: 1
sigaltstack: 1
clone: 1
-setpriority: 1
-getuid32: 1
-fstat64: 1
-fstatfs64: 1
-pread64: 1
-faccessat: 1
-readlinkat: 1
exit: 1
-rt_sigprocmask: 1
-set_tid_address: 1
-restart_syscall: 1
-exit_group: 1
-rt_sigreturn: 1
-pipe2: 1
-gettimeofday: 1
-sched_yield: 1
-nanosleep: 1
lseek: 1
-_llseek: 1
-sched_get_priority_max: 1
-sched_get_priority_min: 1
-statfs64: 1
-sched_setscheduler: 1
-fstatat64: 1
-ugetrlimit: 1
+rt_sigprocmask: 1
+openat: 1
+write: 1
+nanosleep: 1
+setpriority: 1
+set_tid_address: 1
getdents64: 1
+readlinkat: 1
+read: 1
+pread64: 1
+gettimeofday: 1
+faccessat: 1
+exit_group: 1
+restart_syscall: 1
+rt_sigreturn: 1
getrandom: 1
+madvise: 1
-@include /system/etc/seccomp_policy/crash_dump.arm.policy
+# crash dump policy additions
+clock_gettime: 1
+getpid: 1
+gettid: 1
+pipe2: 1
+recvmsg: 1
+process_vm_readv: 1
+tgkill: 1
+rt_sigaction: 1
+rt_tgsigqueueinfo: 1
+#mprotect: arg2 in 0x1|0x2
+munmap: 1
+#mmap: arg2 in 0x1|0x2
+geteuid: 1
+getgid: 1
+getegid: 1
+getgroups: 1
diff --git a/media/codec2/hidl/services/seccomp_policy/codec2.software.base-x86.policy b/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-x86.policy
similarity index 81%
rename from media/codec2/hidl/services/seccomp_policy/codec2.software.base-x86.policy
rename to media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-x86.policy
index 20c7625..d9c4045 100644
--- a/media/codec2/hidl/services/seccomp_policy/codec2.software.base-x86.policy
+++ b/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-x86.policy
@@ -1,4 +1,4 @@
-# Copyright (C) 2018 The Android Open Source Project
+# 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.
@@ -16,14 +16,22 @@
mprotect: 1
prctl: 1
openat: 1
+open: 1
getuid32: 1
+getuid: 1
+getrlimit: 1
writev: 1
ioctl: 1
close: 1
mmap2: 1
+mmap: 1
fstat64: 1
+fstat: 1
+stat64: 1
+statfs64: 1
madvise: 1
fstatat64: 1
+newfstatat: 1
futex: 1
munmap: 1
faccessat: 1
@@ -37,15 +45,22 @@
exit_group: 1
rt_sigreturn: 1
ugetrlimit: 1
+readlink: 1
readlinkat: 1
_llseek: 1
fstatfs64: 1
+fstatfs: 1
pread64: 1
mremap: 1
dup: 1
set_tid_address: 1
write: 1
nanosleep: 1
+sched_setscheduler: 1
+uname: 1
+memfd_create: 1
+ftruncate: 1
+ftruncate64: 1
# Required by AddressSanitizer
gettid: 1
@@ -54,4 +69,3 @@
gettid: 1
@include /system/etc/seccomp_policy/crash_dump.x86.policy
-
diff --git a/media/codec2/hidl/services/seccomp_policy/codec2.software.base-x86.policy b/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-x86_64.policy
similarity index 81%
copy from media/codec2/hidl/services/seccomp_policy/codec2.software.base-x86.policy
copy to media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-x86_64.policy
index 20c7625..d9c4045 100644
--- a/media/codec2/hidl/services/seccomp_policy/codec2.software.base-x86.policy
+++ b/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-x86_64.policy
@@ -1,4 +1,4 @@
-# Copyright (C) 2018 The Android Open Source Project
+# 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.
@@ -16,14 +16,22 @@
mprotect: 1
prctl: 1
openat: 1
+open: 1
getuid32: 1
+getuid: 1
+getrlimit: 1
writev: 1
ioctl: 1
close: 1
mmap2: 1
+mmap: 1
fstat64: 1
+fstat: 1
+stat64: 1
+statfs64: 1
madvise: 1
fstatat64: 1
+newfstatat: 1
futex: 1
munmap: 1
faccessat: 1
@@ -37,15 +45,22 @@
exit_group: 1
rt_sigreturn: 1
ugetrlimit: 1
+readlink: 1
readlinkat: 1
_llseek: 1
fstatfs64: 1
+fstatfs: 1
pread64: 1
mremap: 1
dup: 1
set_tid_address: 1
write: 1
nanosleep: 1
+sched_setscheduler: 1
+uname: 1
+memfd_create: 1
+ftruncate: 1
+ftruncate64: 1
# Required by AddressSanitizer
gettid: 1
@@ -54,4 +69,3 @@
gettid: 1
@include /system/etc/seccomp_policy/crash_dump.x86.policy
-
diff --git a/media/codec2/hidl/services/seccomp_policy/codec2.vendor.base-arm.policy b/media/codec2/hidl/services/seccomp_policy/codec2.vendor.base-arm.policy
deleted file mode 100644
index d5871d1..0000000
--- a/media/codec2/hidl/services/seccomp_policy/codec2.vendor.base-arm.policy
+++ /dev/null
@@ -1,73 +0,0 @@
-# Copyright (C) 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.
-
-# Organized by frequency of systemcall - in descending order for
-# best performance.
-futex: 1
-ioctl: 1
-write: 1
-prctl: 1
-clock_gettime: 1
-getpriority: 1
-read: 1
-close: 1
-writev: 1
-dup: 1
-ppoll: 1
-mmap2: 1
-getrandom: 1
-
-# mremap: Ensure |flags| are (MREMAP_MAYMOVE | MREMAP_FIXED) TODO: Once minijail
-# parser support for '<' is in this needs to be modified to also prevent
-# |old_address| and |new_address| from touching the exception vector page, which
-# on ARM is statically loaded at 0xffff 0000. See
-# http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0211h/Babfeega.html
-# for more details.
-mremap: arg3 == 3
-munmap: 1
-mprotect: 1
-madvise: 1
-openat: 1
-sigaltstack: 1
-clone: 1
-setpriority: 1
-getuid32: 1
-fstat64: 1
-fstatfs64: 1
-pread64: 1
-faccessat: 1
-readlinkat: 1
-exit: 1
-rt_sigprocmask: 1
-set_tid_address: 1
-restart_syscall: 1
-exit_group: 1
-rt_sigreturn: 1
-pipe2: 1
-gettimeofday: 1
-sched_yield: 1
-nanosleep: 1
-lseek: 1
-_llseek: 1
-sched_get_priority_max: 1
-sched_get_priority_min: 1
-statfs64: 1
-sched_setscheduler: 1
-fstatat64: 1
-ugetrlimit: 1
-getdents64: 1
-getrandom: 1
-
-@include /system/etc/seccomp_policy/crash_dump.arm.policy
-
diff --git a/media/codec2/hidl/services/seccomp_policy/codec2.vendor.base-x86.policy b/media/codec2/hidl/services/seccomp_policy/codec2.vendor.base-x86.policy
deleted file mode 100644
index 20c7625..0000000
--- a/media/codec2/hidl/services/seccomp_policy/codec2.vendor.base-x86.policy
+++ /dev/null
@@ -1,57 +0,0 @@
-# Copyright (C) 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.
-
-read: 1
-mprotect: 1
-prctl: 1
-openat: 1
-getuid32: 1
-writev: 1
-ioctl: 1
-close: 1
-mmap2: 1
-fstat64: 1
-madvise: 1
-fstatat64: 1
-futex: 1
-munmap: 1
-faccessat: 1
-_llseek: 1
-lseek: 1
-clone: 1
-sigaltstack: 1
-setpriority: 1
-restart_syscall: 1
-exit: 1
-exit_group: 1
-rt_sigreturn: 1
-ugetrlimit: 1
-readlinkat: 1
-_llseek: 1
-fstatfs64: 1
-pread64: 1
-mremap: 1
-dup: 1
-set_tid_address: 1
-write: 1
-nanosleep: 1
-
-# Required by AddressSanitizer
-gettid: 1
-sched_yield: 1
-getpid: 1
-gettid: 1
-
-@include /system/etc/seccomp_policy/crash_dump.x86.policy
-
diff --git a/media/codec2/hidl/services/vendor.cpp b/media/codec2/hidl/services/vendor.cpp
index a4e079d..81bffeb 100644
--- a/media/codec2/hidl/services/vendor.cpp
+++ b/media/codec2/hidl/services/vendor.cpp
@@ -15,27 +15,29 @@
*/
//#define LOG_NDEBUG 0
-#define LOG_TAG "android.hardware.media.c2@1.0-service"
+#define LOG_TAG "android.hardware.media.c2@1.1-service"
-#include <codec2/hidl/1.0/ComponentStore.h>
-#include <hidl/HidlTransportSupport.h>
+#include <android-base/logging.h>
#include <binder/ProcessState.h>
+#include <codec2/hidl/1.1/ComponentStore.h>
+#include <hidl/HidlTransportSupport.h>
#include <minijail.h>
#include <util/C2InterfaceHelper.h>
#include <C2Component.h>
#include <C2Config.h>
-// OmxStore is added for visibility by dumpstate.
-#include <media/stagefright/omx/1.0/OmxStore.h>
-
-// This is created by module "codec2.vendor.base.policy". This can be modified.
+// This is the absolute on-device path of the prebuild_etc module
+// "android.hardware.media.c2@1.1-default-seccomp_policy" in Android.bp.
static constexpr char kBaseSeccompPolicyPath[] =
- "/vendor/etc/seccomp_policy/codec2.vendor.base.policy";
+ "/vendor/etc/seccomp_policy/"
+ "android.hardware.media.c2@1.1-default-seccomp-policy";
-// Additional device-specific seccomp permissions can be added in this file.
+// Additional seccomp permissions can be added in this file.
+// This file does not exist by default.
static constexpr char kExtSeccompPolicyPath[] =
- "/vendor/etc/seccomp_policy/codec2.vendor.ext.policy";
+ "/vendor/etc/seccomp_policy/"
+ "android.hardware.media.c2@1.1-extended-seccomp-policy";
class StoreImpl : public C2ComponentStore {
public:
@@ -140,54 +142,48 @@
};
int main(int /* argc */, char** /* argv */) {
- ALOGD("android.hardware.media.c2@1.0-service starting...");
+ using namespace ::android;
+ LOG(DEBUG) << "android.hardware.media.c2@1.1-service starting...";
+ // Set up minijail to limit system calls.
signal(SIGPIPE, SIG_IGN);
- android::SetUpMinijail(kBaseSeccompPolicyPath, kExtSeccompPolicyPath);
+ SetUpMinijail(kBaseSeccompPolicyPath, kExtSeccompPolicyPath);
- // vndbinder is needed by BufferQueue.
- android::ProcessState::initWithDriver("/dev/vndbinder");
- android::ProcessState::self()->startThreadPool();
+ // Enable vndbinder to allow vendor-to-vendor binder calls.
+ ProcessState::initWithDriver("/dev/vndbinder");
+ ProcessState::self()->startThreadPool();
// Extra threads may be needed to handle a stacked IPC sequence that
// contains alternating binder and hwbinder calls. (See b/35283480.)
- android::hardware::configureRpcThreadpool(8, true /* callerWillJoin */);
+ hardware::configureRpcThreadpool(8, true /* callerWillJoin */);
// Create IComponentStore service.
{
- using namespace ::android::hardware::media::c2::V1_0;
- android::sp<IComponentStore> store;
+ using namespace ::android::hardware::media::c2::V1_1;
+ sp<IComponentStore> store;
- // Vendor's TODO: Replace this with
+ // TODO: Replace this with
// store = new utils::ComponentStore(
// /* implementation of C2ComponentStore */);
- ALOGD("Instantiating Codec2's dummy IComponentStore service...");
+ LOG(DEBUG) << "Instantiating Codec2's IComponentStore service...";
store = new utils::ComponentStore(
std::make_shared<StoreImpl>());
if (store == nullptr) {
- ALOGE("Cannot create Codec2's IComponentStore service.");
+ LOG(ERROR) << "Cannot create Codec2's IComponentStore service.";
} else {
- if (store->registerAsService("default") != android::OK) {
- ALOGE("Cannot register Codec2's "
- "IComponentStore service.");
+ constexpr char const* serviceName = "default";
+ if (store->registerAsService(serviceName) != OK) {
+ LOG(ERROR) << "Cannot register Codec2's IComponentStore service"
+ " with instance name << \""
+ << serviceName << "\".";
} else {
- ALOGI("Codec2's IComponentStore service created.");
+ LOG(DEBUG) << "Codec2's IComponentStore service registered. "
+ "Instance name: \"" << serviceName << "\".";
}
}
}
- // Register IOmxStore service.
- {
- using namespace ::android::hardware::media::omx::V1_0;
- android::sp<IOmxStore> omxStore = new implementation::OmxStore();
- if (omxStore == nullptr) {
- ALOGE("Cannot create IOmxStore HAL service.");
- } else if (omxStore->registerAsService() != android::OK) {
- ALOGE("Cannot register IOmxStore HAL service.");
- }
- }
-
- android::hardware::joinRpcThreadpool();
+ hardware::joinRpcThreadpool();
return 0;
}
diff --git a/media/codec2/sfplugin/Android.bp b/media/codec2/sfplugin/Android.bp
index ec576c9..534c1a7 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",
@@ -9,10 +11,8 @@
"CCodecConfig.cpp",
"Codec2Buffer.cpp",
"Codec2InfoBuilder.cpp",
- "Omx2IGraphicBufferSource.cpp",
"PipelineWatcher.cpp",
"ReflectedParamUpdater.cpp",
- "SkipCutBuffer.cpp",
],
cflags: [
@@ -28,7 +28,7 @@
shared_libs: [
"android.hardware.cas.native@1.0",
- "android.hardware.graphics.bufferqueue@1.0",
+ "android.hardware.drm@1.0",
"android.hardware.media.c2@1.0",
"android.hardware.media.omx@1.0",
"libbase",
@@ -48,12 +48,16 @@
"libstagefright_codecbase",
"libstagefright_foundation",
"libstagefright_omx",
- "libstagefright_omx_utils",
"libstagefright_xmlparser",
"libui",
"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 4a31953..5c572e1 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -26,8 +26,8 @@
#include <C2ParamInternal.h>
#include <C2PlatformSupport.h>
-#include <android/IGraphicBufferSource.h>
#include <android/IOMXBufferSource.h>
+#include <android/hardware/media/c2/1.0/IInputSurface.h>
#include <android/hardware/media/omx/1.0/IGraphicBufferSource.h>
#include <android/hardware/media/omx/1.0/IOmx.h>
#include <android-base/stringprintf.h>
@@ -35,17 +35,20 @@
#include <gui/IGraphicBufferProducer.h>
#include <gui/Surface.h>
#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
-#include <media/omx/1.0/WGraphicBufferSource.h>
+#include <media/omx/1.0/WOmxNode.h>
+#include <media/openmax/OMX_Core.h>
#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 "InputSurfaceWrapper.h"
-#include "Omx2IGraphicBufferSource.h"
extern "C" android::PersistentSurface *CreateInputSurface();
@@ -54,9 +57,11 @@
using namespace std::chrono_literals;
using ::android::hardware::graphics::bufferqueue::V1_0::utils::H2BGraphicBufferProducer;
using android::base::StringPrintf;
-using BGraphicBufferSource = ::android::IGraphicBufferSource;
using ::android::hardware::media::c2::V1_0::IInputSurface;
+typedef hardware::media::omx::V1_0::IGraphicBufferSource HGraphicBufferSource;
+typedef CCodecConfig Config;
+
namespace {
class CCodecWatchdog : public AHandler {
@@ -180,9 +185,10 @@
class GraphicBufferSourceWrapper : public InputSurfaceWrapper {
public:
-// explicit GraphicBufferSourceWrapper(const sp<BGraphicBufferSource> &source) : mSource(source) {}
+ typedef hardware::media::omx::V1_0::Status OmxStatus;
+
GraphicBufferSourceWrapper(
- const sp<BGraphicBufferSource> &source,
+ const sp<HGraphicBufferSource> &source,
uint32_t width,
uint32_t height,
uint64_t usage)
@@ -194,6 +200,7 @@
status_t connect(const std::shared_ptr<Codec2Client::Component> &comp) override {
mNode = new C2OMXNode(comp);
+ mOmxNode = new hardware::media::omx::V1_0::utils::TWOmxNode(mNode);
mNode->setFrameSize(mWidth, mHeight);
// Usage is queried during configure(), so setting it beforehand.
@@ -204,7 +211,8 @@
// NOTE: we do not use/pass through color aspects from GraphicBufferSource as we
// communicate that directly to the component.
- mSource->configure(mNode, mDataSpace);
+ mSource->configure(
+ mOmxNode, static_cast<hardware::graphics::common::V1_0::Dataspace>(mDataSpace));
return OK;
}
@@ -220,21 +228,16 @@
source->onOmxIdle();
source->onOmxLoaded();
mNode.clear();
+ mOmxNode.clear();
}
- status_t GetStatus(const binder::Status &status) {
- status_t err = OK;
- if (!status.isOk()) {
- err = status.serviceSpecificErrorCode();
- if (err == OK) {
- err = status.transactionError();
- if (err == OK) {
- // binder status failed, but there is no servie or transaction error
- err = UNKNOWN_ERROR;
- }
- }
+ status_t GetStatus(hardware::Return<OmxStatus> &&status) {
+ if (status.isOk()) {
+ return static_cast<status_t>(status.withDefault(OmxStatus::UNKNOWN_ERROR));
+ } else if (status.isDeadObject()) {
+ return DEAD_OBJECT;
}
- return err;
+ return UNKNOWN_ERROR;
}
status_t start() override {
@@ -359,7 +362,15 @@
err = res;
} else {
status << " delayUs";
- res = GetStatus(mSource->getStopTimeOffsetUs(&config.mInputDelayUs));
+ hardware::Return<void> trans = mSource->getStopTimeOffsetUs(
+ [&res, &delayUs = config.mInputDelayUs](
+ auto status, auto stopTimeOffsetUs) {
+ res = static_cast<status_t>(status);
+ delayUs = stopTimeOffsetUs;
+ });
+ if (!trans.isOk()) {
+ res = trans.isDeadObject() ? DEAD_OBJECT : UNKNOWN_ERROR;
+ }
if (res != OK) {
status << " (=> " << asString(res) << ")";
} else {
@@ -388,8 +399,9 @@
}
private:
- sp<BGraphicBufferSource> mSource;
+ sp<HGraphicBufferSource> mSource;
sp<C2OMXNode> mNode;
+ sp<hardware::media::omx::V1_0::IOmxNode> mOmxNode;
uint32_t mWidth;
uint32_t mHeight;
Config mConfig;
@@ -561,7 +573,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() {
@@ -652,7 +665,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");
@@ -726,7 +740,8 @@
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;
// Enforce required parameters
@@ -1042,7 +1057,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);
}
@@ -1092,9 +1108,7 @@
gbs = source;
});
if (transStatus.isOk() && s == OmxStatus::OK) {
- return new PersistentSurface(
- new H2BGraphicBufferProducer(gbp),
- sp<::android::IGraphicBufferSource>(new LWGraphicBufferSource(gbs)));
+ return new PersistentSurface(new H2BGraphicBufferProducer(gbp), gbs);
}
return nullptr;
@@ -1118,35 +1132,36 @@
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;
}
sp<PersistentSurface> persistentSurface = CreateCompatibleInputSurface();
+ sp<hidl::base::V1_0::IBase> hidlTarget = persistentSurface->getHidlTarget();
+ sp<IInputSurface> hidlInputSurface = IInputSurface::castFrom(hidlTarget);
+ sp<HGraphicBufferSource> gbs = HGraphicBufferSource::castFrom(hidlTarget);
- if (persistentSurface->getHidlTarget()) {
- sp<IInputSurface> hidlInputSurface = IInputSurface::castFrom(
- persistentSurface->getHidlTarget());
- if (!hidlInputSurface) {
- ALOGE("Corrupted input surface");
- mCallback->onInputSurfaceCreationFailed(UNKNOWN_ERROR);
- return;
- }
+ if (hidlInputSurface) {
std::shared_ptr<Codec2Client::InputSurface> inputSurface =
std::make_shared<Codec2Client::InputSurface>(hidlInputSurface);
err = setupInputSurface(std::make_shared<C2InputSurfaceWrapper>(
inputSurface));
bufferProducer = inputSurface->getGraphicBufferProducer();
- } else {
+ } else if (gbs) {
int32_t width = 0;
(void)outputFormat->findInt32("width", &width);
int32_t height = 0;
(void)outputFormat->findInt32("height", &height);
err = setupInputSurface(std::make_shared<GraphicBufferSourceWrapper>(
- persistentSurface->getBufferSource(), width, height, usage));
+ gbs, width, height, usage));
bufferProducer = persistentSurface->getBufferProducer();
+ } else {
+ ALOGE("Corrupted input surface");
+ mCallback->onInputSurfaceCreationFailed(UNKNOWN_ERROR);
+ return;
}
if (err != OK) {
@@ -1162,7 +1177,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
@@ -1207,20 +1223,16 @@
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;
}
- auto hidlTarget = surface->getHidlTarget();
- if (hidlTarget) {
- sp<IInputSurface> inputSurface =
- IInputSurface::castFrom(hidlTarget);
- if (!inputSurface) {
- ALOGE("Failed to set input surface: Corrupted surface.");
- mCallback->onInputSurfaceDeclined(UNKNOWN_ERROR);
- return;
- }
+ sp<hidl::base::V1_0::IBase> hidlTarget = surface->getHidlTarget();
+ sp<IInputSurface> inputSurface = IInputSurface::castFrom(hidlTarget);
+ sp<HGraphicBufferSource> gbs = HGraphicBufferSource::castFrom(hidlTarget);
+ if (inputSurface) {
status_t err = setupInputSurface(std::make_shared<C2InputSurfaceWrapper>(
std::make_shared<Codec2Client::InputSurface>(inputSurface)));
if (err != OK) {
@@ -1228,18 +1240,22 @@
mCallback->onInputSurfaceDeclined(err);
return;
}
- } else {
+ } else if (gbs) {
int32_t width = 0;
(void)outputFormat->findInt32("width", &width);
int32_t height = 0;
(void)outputFormat->findInt32("height", &height);
status_t err = setupInputSurface(std::make_shared<GraphicBufferSourceWrapper>(
- surface->getBufferSource(), width, height, usage));
+ gbs, width, height, usage));
if (err != OK) {
ALOGE("Failed to set up input surface: %d", err);
mCallback->onInputSurfaceDeclined(err);
return;
}
+ } else {
+ ALOGE("Failed to set input surface: Corrupted surface.");
+ mCallback->onInputSurfaceDeclined(UNKNOWN_ERROR);
+ return;
}
mCallback->onInputSurfaceAccepted(inputFormat, outputFormat);
}
@@ -1284,7 +1300,8 @@
sp<AMessage> outputFormat;
status_t err2 = OK;
{
- 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) {
@@ -1370,7 +1387,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;
@@ -1418,7 +1436,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;
@@ -1517,7 +1536,9 @@
{
Mutexed<State>::Locked state(mState);
- state->set(FLUSHED);
+ if (state->get() == FLUSHING) {
+ state->set(FLUSHED);
+ }
}
mCallback->onFlushCompleted();
}
@@ -1574,7 +1595,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
@@ -1633,7 +1655,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));
@@ -1652,7 +1675,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);
}
@@ -1729,7 +1753,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>();
@@ -1858,15 +1883,10 @@
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.
std::shared_ptr<Codec2Client::InputSurface> inputSurface =
Codec2Client::CreateInputSurface();
@@ -1880,9 +1900,7 @@
return nullptr;
}
return new PersistentSurface(
- gbs->getIGraphicBufferProducer(),
- sp<IGraphicBufferSource>(
- new Omx2IGraphicBufferSource(gbs)));
+ gbs->getIGraphicBufferProducer(), new TWGraphicBufferSource(gbs));
} else {
return nullptr;
}
@@ -1893,3 +1911,5 @@
inputSurface->getHalInterface()));
}
+} // namespace android
+
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 375ce17..3218491 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -27,10 +27,12 @@
#include <C2Debug.h>
#include <android/hardware/cas/native/1.0/IDescrambler.h>
+#include <android/hardware/drm/1.0/types.h>
#include <android-base/stringprintf.h>
#include <binder/MemoryDealer.h>
#include <cutils/properties.h>
#include <gui/Surface.h>
+#include <hidlmemory/FrameworkUtils.h>
#include <media/openmax/OMX_Core.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ALookup.h>
@@ -39,12 +41,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 {
@@ -52,10 +54,14 @@
using hardware::hidl_handle;
using hardware::hidl_string;
using hardware::hidl_vec;
+using hardware::fromHeap;
+using hardware::HidlMemory;
+
using namespace hardware::cas::V1_0;
using namespace hardware::cas::native::V1_0;
using CasStatus = hardware::cas::V1_0::Status;
+using DrmBufferType = hardware::drm::V1_0::BufferType;
namespace {
@@ -431,15 +437,16 @@
ssize_t result = -1;
ssize_t codecDataOffset = 0;
if (mCrypto != nullptr) {
- ICrypto::DestinationBuffer destination;
+ hardware::drm::V1_0::DestinationBuffer destination;
if (secure) {
- destination.mType = ICrypto::kDestinationTypeNativeHandle;
- destination.mHandle = encryptedBuffer->handle();
+ destination.type = DrmBufferType::NATIVE_HANDLE;
+ destination.secureMemory = hidl_handle(encryptedBuffer->handle());
} else {
- destination.mType = ICrypto::kDestinationTypeSharedMemory;
- destination.mSharedMemory = mDecryptDestination;
+ destination.type = DrmBufferType::SHARED_MEMORY;
+ IMemoryToSharedBuffer(
+ mDecryptDestination, mHeapSeqNum, &destination.nonsecureMemory);
}
- ICrypto::SourceBuffer source;
+ hardware::drm::V1_0::SharedBuffer source;
encryptedBuffer->fillSourceBuffer(&source);
result = mCrypto->decrypt(
key, iv, mode, pattern, source, buffer->offset(),
@@ -447,7 +454,7 @@
if (result < 0) {
return result;
}
- if (destination.mType == ICrypto::kDestinationTypeSharedMemory) {
+ if (destination.type == DrmBufferType::SHARED_MEMORY) {
encryptedBuffer->copyDecryptedContent(mDecryptDestination, result);
}
} else {
@@ -819,10 +826,8 @@
bool secure = mComponent->getName().find(".secure") != std::string::npos;
std::shared_ptr<C2AllocatorStore> allocatorStore = GetCodec2PlatformAllocatorStore();
- int poolMask = property_get_int32(
- "debug.stagefright.c2-poolmask",
- 1 << C2PlatformAllocatorStore::ION |
- 1 << C2PlatformAllocatorStore::BUFFERQUEUE);
+ int poolMask = GetCodec2PoolMask();
+ C2PlatformAllocatorStore::id_t preferredLinearId = GetPreferredLinearAllocatorId(poolMask);
if (inputFormat != nullptr) {
bool graphic = (iStreamFormat.value == C2BufferData::GRAPHIC);
@@ -832,7 +837,7 @@
// set default allocator ID.
pools->inputAllocatorId = (graphic) ? C2PlatformAllocatorStore::GRALLOC
- : C2PlatformAllocatorStore::ION;
+ : preferredLinearId;
// query C2PortAllocatorsTuning::input from component. If an allocator ID is obtained
// from component, create the input block pool with given ID. Otherwise, use default IDs.
@@ -919,7 +924,8 @@
mDecryptDestination = mDealer->allocate((size_t)capacity);
}
if (mCrypto != nullptr && mHeapSeqNum < 0) {
- mHeapSeqNum = mCrypto->setHeap(mDealer->getMemoryHeap());
+ sp<HidlMemory> heap = fromHeap(mDealer->getMemoryHeap());
+ mHeapSeqNum = mCrypto->setHeap(heap);
} else {
mHeapSeqNum = -1;
}
@@ -970,7 +976,7 @@
// set default allocator ID.
pools->outputAllocatorId = (graphic) ? C2PlatformAllocatorStore::GRALLOC
- : C2PlatformAllocatorStore::ION;
+ : preferredLinearId;
// query C2PortAllocatorsTuning::output from component, or use default allocator if
// unsuccessful.
@@ -1084,8 +1090,7 @@
outputGeneration);
}
- if (oStreamFormat.value == C2BufferData::LINEAR
- && mComponentName.find("c2.qti.") == std::string::npos) {
+ if (oStreamFormat.value == C2BufferData::LINEAR) {
// WORKAROUND: if we're using early CSD workaround we convert to
// array mode, to appease apps assuming the output
// buffers to be of the same size.
@@ -1137,8 +1142,9 @@
}
C2StreamBufferTypeSetting::output oStreamFormat(0u);
- c2_status_t err = mComponent->query({ &oStreamFormat }, {}, C2_DONT_BLOCK, nullptr);
- if (err != C2_OK) {
+ C2PrependHeaderModeSetting prepend(PREPEND_HEADER_TO_NONE);
+ c2_status_t err = mComponent->query({ &oStreamFormat, &prepend }, {}, C2_DONT_BLOCK, nullptr);
+ if (err != C2_OK && err != C2_BAD_INDEX) {
return UNKNOWN_ERROR;
}
size_t numInputSlots = mInput.lock()->numSlots;
@@ -1178,7 +1184,7 @@
mName, buffer->capacity(), config->size());
}
} else if (oStreamFormat.value == C2BufferData::LINEAR && i == 0
- && mComponentName.find("c2.qti.") == std::string::npos) {
+ && (!prepend || prepend.value == PREPEND_HEADER_TO_NONE)) {
// WORKAROUND: Some apps expect CSD available without queueing
// any input. Queue an empty buffer to get the CSD.
buffer->setRange(0, 0);
@@ -1662,6 +1668,14 @@
mMetaMode = mode;
}
+void CCodecBufferChannel::setCrypto(const sp<ICrypto> &crypto) {
+ mCrypto = crypto;
+}
+
+void CCodecBufferChannel::setDescrambler(const sp<IDescrambler> &descrambler) {
+ mDescrambler = descrambler;
+}
+
status_t toStatusT(c2_status_t c2s, c2_operation_t c2op) {
// C2_OK is always translated to OK.
if (c2s == C2_OK) {
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h
index c0fa138..82fec18 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.h
+++ b/media/codec2/sfplugin/CCodecBufferChannel.h
@@ -56,6 +56,9 @@
virtual ~CCodecBufferChannel();
// BufferChannelBase interface
+ void setCrypto(const sp<ICrypto> &crypto) override;
+ void setDescrambler(const sp<IDescrambler> &descrambler) override;
+
virtual status_t queueInputBuffer(const sp<MediaCodecBuffer> &buffer) override;
virtual status_t queueSecureInputBuffer(
const sp<MediaCodecBuffer> &buffer,
@@ -318,6 +321,9 @@
std::atomic_bool mInputMetEos;
std::once_flag mRenderWarningFlag;
+ sp<ICrypto> mCrypto;
+ sp<IDescrambler> mDescrambler;
+
inline bool hasCryptoOrDescrambler() {
return mCrypto != nullptr || mDescrambler != nullptr;
}
diff --git a/media/codec2/sfplugin/CCodecBuffers.cpp b/media/codec2/sfplugin/CCodecBuffers.cpp
index ed8b832..2a04810 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"
diff --git a/media/codec2/sfplugin/CCodecBuffers.h b/media/codec2/sfplugin/CCodecBuffers.h
index 2cb6b81..ad972ce 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;
diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp
index 5adcd94..ee3cdf6 100644
--- a/media/codec2/sfplugin/CCodecConfig.cpp
+++ b/media/codec2/sfplugin/CCodecConfig.cpp
@@ -823,6 +823,14 @@
add(ConfigMapper(C2_PARAMKEY_INPUT_TIME_STRETCH, C2_PARAMKEY_INPUT_TIME_STRETCH, "value"));
+ add(ConfigMapper(KEY_LOW_LATENCY, C2_PARAMKEY_LOW_LATENCY_MODE, "value")
+ .limitTo(D::DECODER & (D::CONFIG | D::PARAM))
+ .withMapper([](C2Value v) -> C2Value {
+ int32_t value = 0;
+ (void)v.get(&value);
+ return value == 0 ? C2_FALSE : C2_TRUE;
+ }));
+
/* still to do
constexpr char KEY_PUSH_BLANK_BUFFERS_ON_STOP[] = "push-blank-buffers-on-shutdown";
diff --git a/media/codec2/sfplugin/Codec2Buffer.cpp b/media/codec2/sfplugin/Codec2Buffer.cpp
index 5c8ad56..b6d18e2 100644
--- a/media/codec2/sfplugin/Codec2Buffer.cpp
+++ b/media/codec2/sfplugin/Codec2Buffer.cpp
@@ -20,6 +20,7 @@
#include <hidlmemory/FrameworkUtils.h>
#include <media/hardware/HardwareAPI.h>
+#include <media/stagefright/CodecBase.h>
#include <media/stagefright/MediaCodecConstants.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/AMessage.h>
@@ -764,7 +765,11 @@
const std::shared_ptr<C2LinearBlock> &block,
const sp<IMemory> &memory,
int32_t heapSeqNum)
- : Codec2Buffer(format, new ABuffer(memory->pointer(), memory->size())),
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ : Codec2Buffer(format, new ABuffer(memory->unsecurePointer(), memory->size())),
mBlock(block),
mMemory(memory),
mHeapSeqNum(heapSeqNum) {
@@ -775,9 +780,8 @@
}
void EncryptedLinearBlockBuffer::fillSourceBuffer(
- ICrypto::SourceBuffer *source) {
- source->mSharedMemory = mMemory;
- source->mHeapSeqNum = mHeapSeqNum;
+ hardware::drm::V1_0::SharedBuffer *source) {
+ BufferChannelBase::IMemoryToSharedBuffer(mMemory, mHeapSeqNum, source);
}
void EncryptedLinearBlockBuffer::fillSourceBuffer(
@@ -800,7 +804,7 @@
if (view.size() < length) {
return false;
}
- memcpy(view.data(), decrypted->pointer(), length);
+ memcpy(view.data(), decrypted->unsecurePointer(), length);
return true;
}
diff --git a/media/codec2/sfplugin/Codec2Buffer.h b/media/codec2/sfplugin/Codec2Buffer.h
index 6f87101..9291c52 100644
--- a/media/codec2/sfplugin/Codec2Buffer.h
+++ b/media/codec2/sfplugin/Codec2Buffer.h
@@ -21,6 +21,7 @@
#include <C2Buffer.h>
#include <android/hardware/cas/native/1.0/types.h>
+#include <android/hardware/drm/1.0/types.h>
#include <binder/IMemory.h>
#include <media/hardware/VideoAPI.h>
#include <media/stagefright/foundation/ABuffer.h>
@@ -361,7 +362,8 @@
*
* \param source source buffer structure to fill.
*/
- void fillSourceBuffer(ICrypto::SourceBuffer *source);
+ void fillSourceBuffer(
+ hardware::drm::V1_0::SharedBuffer *source);
void fillSourceBuffer(
hardware::cas::native::V1_0::SharedBuffer *source);
diff --git a/media/codec2/sfplugin/Codec2InfoBuilder.cpp b/media/codec2/sfplugin/Codec2InfoBuilder.cpp
index 6b75eba..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 {
@@ -117,8 +116,9 @@
}
}
- // For VP9, the static info is always propagated by framework.
+ // For VP9/AV1, the static info is always propagated by framework.
supportsHdr |= (mediaType == MIMETYPE_VIDEO_VP9);
+ supportsHdr |= (mediaType == MIMETYPE_VIDEO_AV1);
for (C2Value::Primitive profile : profileQuery[0].values.values) {
pl.profile = (C2Config::profile_t)profile.ref<uint32_t>();
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 97%
rename from media/codec2/sfplugin/CCodec.h
rename to media/codec2/sfplugin/include/media/stagefright/CCodec.h
index b0b3c4f..30dc945 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,8 @@
void onWorkDone(std::list<std::unique_ptr<C2Work>> &workItems);
void onInputBufferDone(uint64_t frameIndex, size_t arrayIndex);
+ static PersistentSurface *CreateInputSurface();
+
protected:
virtual ~CCodec();
@@ -173,7 +174,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/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 40160c7..2f3d688 100644
--- a/media/codec2/sfplugin/utils/Codec2Mapper.cpp
+++ b/media/codec2/sfplugin/utils/Codec2Mapper.cpp
@@ -190,6 +190,7 @@
{ C2Config::PROFILE_DV_HE_07, DolbyVisionProfileDvheDtb },
{ C2Config::PROFILE_DV_HE_08, DolbyVisionProfileDvheSt },
{ C2Config::PROFILE_DV_AV_09, DolbyVisionProfileDvavSe },
+ { C2Config::PROFILE_DV_AV1_10, DolbyVisionProfileDvav110 },
};
ALookup<C2Config::level_t, int32_t> sH263Levels = {
@@ -382,10 +383,11 @@
// TODO: will need to disambiguate between Main8 and Main10
{ C2Config::PROFILE_AV1_0, AV1ProfileMain8 },
{ C2Config::PROFILE_AV1_0, AV1ProfileMain10 },
+ { C2Config::PROFILE_AV1_0, AV1ProfileMain10HDR10 },
+ { C2Config::PROFILE_AV1_0, AV1ProfileMain10HDR10Plus },
};
ALookup<C2Config::profile_t, int32_t> sAv1HdrProfiles = {
- { C2Config::PROFILE_AV1_0, AV1ProfileMain10 },
{ C2Config::PROFILE_AV1_0, AV1ProfileMain10HDR10 },
};
@@ -629,7 +631,7 @@
// static
std::shared_ptr<C2Mapper::ProfileLevelMapper>
C2Mapper::GetProfileLevelMapper(std::string mediaType) {
- std::transform(mediaType.begin(), mediaType.begin(), mediaType.end(), ::tolower);
+ std::transform(mediaType.begin(), mediaType.end(), mediaType.begin(), ::tolower);
if (mediaType == MIMETYPE_AUDIO_AAC) {
return std::make_shared<AacProfileLevelMapper>();
} else if (mediaType == MIMETYPE_VIDEO_AVC) {
@@ -657,11 +659,13 @@
// static
std::shared_ptr<C2Mapper::ProfileLevelMapper>
C2Mapper::GetHdrProfileLevelMapper(std::string mediaType, bool isHdr10Plus) {
- std::transform(mediaType.begin(), mediaType.begin(), mediaType.end(), ::tolower);
+ std::transform(mediaType.begin(), mediaType.end(), mediaType.begin(), ::tolower);
if (mediaType == MIMETYPE_VIDEO_HEVC) {
return std::make_shared<HevcProfileLevelMapper>(true, isHdr10Plus);
} else if (mediaType == MIMETYPE_VIDEO_VP9) {
return std::make_shared<Vp9ProfileLevelMapper>(true, isHdr10Plus);
+ } else if (mediaType == MIMETYPE_VIDEO_AV1) {
+ return std::make_shared<Av1ProfileLevelMapper>(true, isHdr10Plus);
}
return nullptr;
}
diff --git a/media/codec2/vndk/Android.bp b/media/codec2/vndk/Android.bp
index 6fbad0a..a1f145b 100644
--- a/media/codec2/vndk/Android.bp
+++ b/media/codec2/vndk/Android.bp
@@ -14,8 +14,11 @@
cc_library_shared {
name: "libcodec2_vndk",
vendor_available: true,
+ // TODO: b/147147883
+ double_loadable: true,
srcs: [
+ "C2AllocatorBlob.cpp",
"C2AllocatorIon.cpp",
"C2AllocatorGralloc.cpp",
"C2Buffer.cpp",
@@ -49,14 +52,10 @@
],
shared_libs: [
- "android.hardware.graphics.allocator@2.0",
- "android.hardware.graphics.allocator@3.0",
"android.hardware.graphics.bufferqueue@2.0",
- "android.hardware.graphics.mapper@2.0",
- "android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.common@1.2",
"android.hardware.media.bufferpool@2.0",
"libbase",
- "libbinder",
"libcutils",
"libdl",
"libhardware",
diff --git a/media/codec2/vndk/C2AllocatorBlob.cpp b/media/codec2/vndk/C2AllocatorBlob.cpp
new file mode 100644
index 0000000..50c9e59
--- /dev/null
+++ b/media/codec2/vndk/C2AllocatorBlob.cpp
@@ -0,0 +1,186 @@
+/*
+ * 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 "C2AllocatorBlob"
+
+#include <C2AllocatorBlob.h>
+#include <C2PlatformSupport.h>
+
+#include <android/hardware/graphics/common/1.2/types.h>
+#include <utils/Log.h>
+
+namespace android {
+
+using ::android::hardware::graphics::common::V1_2::PixelFormat;
+
+constexpr uint32_t kLinearBufferHeight = 1u;
+constexpr uint32_t kLinearBufferFormat = static_cast<uint32_t>(PixelFormat::BLOB);
+
+namespace {
+
+c2_status_t GetCapacityFromHandle(const C2Handle* const grallocHandle, size_t* capacity) {
+ uint32_t width, height, format, stride, generation, igbp_slot;
+ uint64_t usage, igbp_id;
+ _UnwrapNativeCodec2GrallocMetadata(grallocHandle, &width, &height, &format, &usage, &stride,
+ &generation, &igbp_id, &igbp_slot);
+
+ if (height != kLinearBufferHeight || format != kLinearBufferFormat) {
+ return C2_BAD_VALUE;
+ }
+ *capacity = width;
+ return C2_OK;
+}
+
+} // namespace
+
+// C2AllocationBlob is a wrapper for C2AllocationGralloc allocated by C2AllocatorGralloc.
+// C2AllocationBlob::handle() delegates to the backed C2AllocationGralloc::handle().
+class C2AllocationBlob : public C2LinearAllocation {
+public:
+ C2AllocationBlob(std::shared_ptr<C2GraphicAllocation> graphicAllocation, size_t capacity,
+ C2Allocator::id_t allocatorId);
+ ~C2AllocationBlob() override;
+ c2_status_t map(size_t offset, size_t size, C2MemoryUsage usage, C2Fence* fence,
+ void** addr /* nonnull */) override;
+ c2_status_t unmap(void* addr, size_t size, C2Fence* fenceFd) override;
+
+ id_t getAllocatorId() const override { return mAllocatorId; }
+ const C2Handle* handle() const override { return mGraphicAllocation->handle(); }
+ bool equals(const std::shared_ptr<C2LinearAllocation>& other) const override {
+ return other && other->handle() == handle();
+ }
+
+private:
+ const std::shared_ptr<C2GraphicAllocation> mGraphicAllocation;
+ const C2Allocator::id_t mAllocatorId;
+};
+
+C2AllocationBlob::C2AllocationBlob(
+ std::shared_ptr<C2GraphicAllocation> graphicAllocation, size_t capacity,
+ C2Allocator::id_t allocatorId)
+ : C2LinearAllocation(capacity),
+ mGraphicAllocation(std::move(graphicAllocation)),
+ mAllocatorId(allocatorId) {}
+
+C2AllocationBlob::~C2AllocationBlob() {}
+
+c2_status_t C2AllocationBlob::map(size_t offset, size_t size, C2MemoryUsage usage,
+ C2Fence* fence, void** addr /* nonnull */) {
+ C2PlanarLayout layout;
+ C2Rect rect = C2Rect(size, kLinearBufferHeight).at(offset, 0u);
+ return mGraphicAllocation->map(rect, usage, fence, &layout, reinterpret_cast<uint8_t**>(addr));
+}
+
+c2_status_t C2AllocationBlob::unmap(void* addr, size_t size, C2Fence* fenceFd) {
+ C2Rect rect(size, kLinearBufferHeight);
+ return mGraphicAllocation->unmap(reinterpret_cast<uint8_t**>(&addr), rect, fenceFd);
+}
+
+/* ====================================== BLOB ALLOCATOR ====================================== */
+C2AllocatorBlob::C2AllocatorBlob(id_t id) {
+ C2MemoryUsage minUsage = {0, 0};
+ C2MemoryUsage maxUsage = {C2MemoryUsage::CPU_READ | C2MemoryUsage::READ_PROTECTED,
+ C2MemoryUsage::CPU_WRITE};
+ Traits traits = {"android.allocator.blob", id, LINEAR, minUsage, maxUsage};
+ mTraits = std::make_shared<C2Allocator::Traits>(traits);
+ auto allocatorStore = GetCodec2PlatformAllocatorStore();
+ allocatorStore->fetchAllocator(C2PlatformAllocatorStore::GRALLOC, &mC2AllocatorGralloc);
+ if (!mC2AllocatorGralloc) {
+ ALOGE("Failed to obtain C2AllocatorGralloc as backed allocator");
+ }
+}
+
+C2AllocatorBlob::~C2AllocatorBlob() {}
+
+c2_status_t C2AllocatorBlob::newLinearAllocation(
+ uint32_t capacity, C2MemoryUsage usage, std::shared_ptr<C2LinearAllocation>* allocation) {
+ if (allocation == nullptr) {
+ return C2_BAD_VALUE;
+ }
+
+ allocation->reset();
+
+ if (!mC2AllocatorGralloc) {
+ return C2_CORRUPTED;
+ }
+
+ std::shared_ptr<C2GraphicAllocation> graphicAllocation;
+ c2_status_t status = mC2AllocatorGralloc->newGraphicAllocation(
+ capacity, kLinearBufferHeight, kLinearBufferFormat, usage, &graphicAllocation);
+ if (status != C2_OK) {
+ ALOGE("Failed newGraphicAllocation");
+ return status;
+ }
+
+ allocation->reset(new C2AllocationBlob(std::move(graphicAllocation),
+ static_cast<size_t>(capacity), mTraits->id));
+ return C2_OK;
+}
+
+c2_status_t C2AllocatorBlob::priorLinearAllocation(
+ const C2Handle* handle, std::shared_ptr<C2LinearAllocation>* allocation) {
+ if (allocation == nullptr) {
+ return C2_BAD_VALUE;
+ }
+
+ allocation->reset();
+
+ if (!mC2AllocatorGralloc) {
+ return C2_CORRUPTED;
+ }
+
+ std::shared_ptr<C2GraphicAllocation> graphicAllocation;
+ c2_status_t status = mC2AllocatorGralloc->priorGraphicAllocation(handle, &graphicAllocation);
+ if (status != C2_OK) {
+ ALOGE("Failed priorGraphicAllocation");
+ return status;
+ }
+
+ const C2Handle* const grallocHandle = graphicAllocation->handle();
+ size_t capacity = 0;
+ status = GetCapacityFromHandle(grallocHandle, &capacity);
+ if (status != C2_OK) {
+ ALOGE("Failed to extract capacity from Handle");
+ return status;
+ }
+
+ allocation->reset(new C2AllocationBlob(std::move(graphicAllocation), capacity, mTraits->id));
+ return C2_OK;
+}
+
+id_t C2AllocatorBlob::getId() const {
+ return mTraits->id;
+}
+
+C2String C2AllocatorBlob::getName() const {
+ return mTraits->name;
+}
+
+std::shared_ptr<const C2Allocator::Traits> C2AllocatorBlob::getTraits() const {
+ return mTraits;
+}
+
+// static
+bool C2AllocatorBlob::isValid(const C2Handle* const o) {
+ size_t capacity;
+ // Distinguish C2Handle purely allocated by C2AllocatorGralloc, or one allocated through
+ // C2AllocatorBlob, by checking the handle's height is 1, and its format is
+ // PixelFormat::BLOB by GetCapacityFromHandle().
+ return C2AllocatorGralloc::isValid(o) && GetCapacityFromHandle(o, &capacity) == C2_OK;
+}
+
+} // namespace android
diff --git a/media/codec2/vndk/C2AllocatorGralloc.cpp b/media/codec2/vndk/C2AllocatorGralloc.cpp
index af97e61..3ac3d89 100644
--- a/media/codec2/vndk/C2AllocatorGralloc.cpp
+++ b/media/codec2/vndk/C2AllocatorGralloc.cpp
@@ -18,17 +18,21 @@
#define LOG_TAG "C2AllocatorGralloc"
#include <utils/Log.h>
-#include <android/hardware/graphics/allocator/2.0/IAllocator.h>
-#include <android/hardware/graphics/mapper/2.0/IMapper.h>
-#include <android/hardware/graphics/allocator/3.0/IAllocator.h>
-#include <android/hardware/graphics/mapper/3.0/IMapper.h>
+#include <mutex>
+
+#include <android/hardware/graphics/common/1.2/types.h>
#include <cutils/native_handle.h>
#include <hardware/gralloc.h>
+#include <ui/GraphicBufferAllocator.h>
+#include <ui/GraphicBufferMapper.h>
#include <C2AllocatorGralloc.h>
#include <C2Buffer.h>
#include <C2PlatformSupport.h>
+using ::android::hardware::hidl_handle;
+using PixelFormat4 = ::android::hardware::graphics::common::V1_2::PixelFormat;
+
namespace android {
namespace /* unnamed */ {
@@ -61,59 +65,9 @@
(expected & PASSTHROUGH_USAGE_MASK));
}
-using ::android::hardware::hidl_handle;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::graphics::common::V1_0::BufferUsage;
-using PixelFormat2 = ::android::hardware::graphics::common::V1_0::PixelFormat;
-using PixelFormat3 = ::android::hardware::graphics::common::V1_2::PixelFormat;
-
-using IAllocator2 = ::android::hardware::graphics::allocator::V2_0::IAllocator;
-using BufferDescriptor2 = ::android::hardware::graphics::mapper::V2_0::BufferDescriptor;
-using Error2 = ::android::hardware::graphics::mapper::V2_0::Error;
-using IMapper2 = ::android::hardware::graphics::mapper::V2_0::IMapper;
-
-using IAllocator3 = ::android::hardware::graphics::allocator::V3_0::IAllocator;
-using BufferDescriptor3 = ::android::hardware::graphics::mapper::V3_0::BufferDescriptor;
-using Error3 = ::android::hardware::graphics::mapper::V3_0::Error;
-using IMapper3 = ::android::hardware::graphics::mapper::V3_0::IMapper;
-
namespace /* unnamed */ {
-struct BufferDescriptorInfo2 {
- IMapper2::BufferDescriptorInfo mapperInfo;
- uint32_t stride;
-};
-
-struct BufferDescriptorInfo3 {
- IMapper3::BufferDescriptorInfo mapperInfo;
- uint32_t stride;
-};
-
/* ===================================== GRALLOC ALLOCATION ==================================== */
-c2_status_t maperr2error(Error2 maperr) {
- switch (maperr) {
- case Error2::NONE: return C2_OK;
- case Error2::BAD_DESCRIPTOR: return C2_BAD_VALUE;
- case Error2::BAD_BUFFER: return C2_BAD_VALUE;
- case Error2::BAD_VALUE: return C2_BAD_VALUE;
- case Error2::NO_RESOURCES: return C2_NO_MEMORY;
- case Error2::UNSUPPORTED: return C2_CANNOT_DO;
- }
- return C2_CORRUPTED;
-}
-
-c2_status_t maperr2error(Error3 maperr) {
- switch (maperr) {
- case Error3::NONE: return C2_OK;
- case Error3::BAD_DESCRIPTOR: return C2_BAD_VALUE;
- case Error3::BAD_BUFFER: return C2_BAD_VALUE;
- case Error3::BAD_VALUE: return C2_BAD_VALUE;
- case Error3::NO_RESOURCES: return C2_NO_MEMORY;
- case Error3::UNSUPPORTED: return C2_CANNOT_DO;
- }
- return C2_CORRUPTED;
-}
-
bool native_handle_is_invalid(const native_handle_t *const handle) {
// perform basic validation of a native handle
if (handle == nullptr) {
@@ -309,15 +263,11 @@
// internal methods
// |handle| will be moved.
+
C2AllocationGralloc(
- const BufferDescriptorInfo2 &info,
- const sp<IMapper2> &mapper,
- hidl_handle &hidlHandle,
- const C2HandleGralloc *const handle,
- C2Allocator::id_t allocatorId);
- C2AllocationGralloc(
- const BufferDescriptorInfo3 &info,
- const sp<IMapper3> &mapper,
+ uint32_t width, uint32_t height,
+ uint32_t format, uint32_t layerCount,
+ uint64_t grallocUsage, uint32_t stride,
hidl_handle &hidlHandle,
const C2HandleGralloc *const handle,
C2Allocator::id_t allocatorId);
@@ -325,10 +275,12 @@
c2_status_t status() const;
private:
- const BufferDescriptorInfo2 mInfo2{};
- const sp<IMapper2> mMapper2{nullptr};
- const BufferDescriptorInfo3 mInfo3{};
- const sp<IMapper3> mMapper3{nullptr};
+ const uint32_t mWidth;
+ const uint32_t mHeight;
+ const uint32_t mFormat;
+ const uint32_t mLayerCount;
+ const uint64_t mGrallocUsage;
+ const uint32_t mStride;
const hidl_handle mHidlHandle;
const C2HandleGralloc *mHandle;
buffer_handle_t mBuffer;
@@ -339,31 +291,19 @@
};
C2AllocationGralloc::C2AllocationGralloc(
- const BufferDescriptorInfo2 &info,
- const sp<IMapper2> &mapper,
+ uint32_t width, uint32_t height,
+ uint32_t format, uint32_t layerCount,
+ uint64_t grallocUsage, uint32_t stride,
hidl_handle &hidlHandle,
const C2HandleGralloc *const handle,
C2Allocator::id_t allocatorId)
- : C2GraphicAllocation(info.mapperInfo.width, info.mapperInfo.height),
- mInfo2(info),
- mMapper2(mapper),
- mHidlHandle(std::move(hidlHandle)),
- mHandle(handle),
- mBuffer(nullptr),
- mLockedHandle(nullptr),
- mLocked(false),
- mAllocatorId(allocatorId) {
-}
-
-C2AllocationGralloc::C2AllocationGralloc(
- const BufferDescriptorInfo3 &info,
- const sp<IMapper3> &mapper,
- hidl_handle &hidlHandle,
- const C2HandleGralloc *const handle,
- C2Allocator::id_t allocatorId)
- : C2GraphicAllocation(info.mapperInfo.width, info.mapperInfo.height),
- mInfo3(info),
- mMapper3(mapper),
+ : C2GraphicAllocation(width, height),
+ mWidth(width),
+ mHeight(height),
+ mFormat(format),
+ mLayerCount(layerCount),
+ mGrallocUsage(grallocUsage),
+ mStride(stride),
mHidlHandle(std::move(hidlHandle)),
mHandle(handle),
mBuffer(nullptr),
@@ -379,16 +319,9 @@
unmap(addr, C2Rect(), nullptr);
}
if (mBuffer) {
- if (mMapper2) {
- if (!mMapper2->freeBuffer(const_cast<native_handle_t *>(
- mBuffer)).isOk()) {
- ALOGE("failed transaction: freeBuffer");
- }
- } else {
- if (!mMapper3->freeBuffer(const_cast<native_handle_t *>(
- mBuffer)).isOk()) {
- ALOGE("failed transaction: freeBuffer");
- }
+ status_t err = GraphicBufferMapper::get().freeBuffer(mBuffer);
+ if (err) {
+ ALOGE("failed transaction: freeBuffer");
}
}
if (mHandle) {
@@ -410,7 +343,7 @@
(long long)usage.expected, (long long)grallocUsage);
// TODO
- (void) fence;
+ (void)fence;
std::lock_guard<std::mutex> lock(mMappedLock);
if (mBuffer && mLocked) {
@@ -422,34 +355,13 @@
return C2_BAD_VALUE;
}
- c2_status_t err = C2_OK;
if (!mBuffer) {
- if (mMapper2) {
- if (!mMapper2->importBuffer(
- mHidlHandle, [&err, this](const auto &maperr, const auto &buffer) {
- err = maperr2error(maperr);
- if (err == C2_OK) {
- mBuffer = static_cast<buffer_handle_t>(buffer);
- }
- }).isOk()) {
- ALOGE("failed transaction: importBuffer");
- return C2_CORRUPTED;
- }
- } else {
- if (!mMapper3->importBuffer(
- mHidlHandle, [&err, this](const auto &maperr, const auto &buffer) {
- err = maperr2error(maperr);
- if (err == C2_OK) {
- mBuffer = static_cast<buffer_handle_t>(buffer);
- }
- }).isOk()) {
- ALOGE("failed transaction: importBuffer (@3.0)");
- return C2_CORRUPTED;
- }
- }
- if (err != C2_OK) {
- ALOGD("importBuffer failed: %d", err);
- return err;
+ status_t err = GraphicBufferMapper::get().importBuffer(
+ mHidlHandle.getNativeHandle(), mWidth, mHeight, mLayerCount,
+ mFormat, mGrallocUsage, mStride, &mBuffer);
+ if (err) {
+ ALOGE("failed transaction: importBuffer");
+ return C2_CORRUPTED;
}
if (mBuffer == nullptr) {
ALOGD("importBuffer returned null buffer");
@@ -461,69 +373,26 @@
if (mHandle) {
mHandle->getIgbpData(&generation, &igbp_id, &igbp_slot);
}
- if (mMapper2) {
- mLockedHandle = C2HandleGralloc::WrapAndMoveNativeHandle(
- mBuffer, mInfo2.mapperInfo.width, mInfo2.mapperInfo.height,
- (uint32_t)mInfo2.mapperInfo.format, mInfo2.mapperInfo.usage,
- mInfo2.stride, generation, igbp_id, igbp_slot);
- } else {
- mLockedHandle = C2HandleGralloc::WrapAndMoveNativeHandle(
- mBuffer, mInfo3.mapperInfo.width, mInfo3.mapperInfo.height,
- (uint32_t)mInfo3.mapperInfo.format, mInfo3.mapperInfo.usage,
- mInfo3.stride, generation, igbp_id, igbp_slot);
- }
- }
- PixelFormat3 format = mMapper2 ?
- PixelFormat3(mInfo2.mapperInfo.format) :
- PixelFormat3(mInfo3.mapperInfo.format);
- switch (format) {
- case PixelFormat3::RGBA_1010102: {
+ mLockedHandle = C2HandleGralloc::WrapAndMoveNativeHandle(
+ mBuffer, mWidth, mHeight, mFormat, mGrallocUsage,
+ mStride, generation, igbp_id, igbp_slot);
+ }
+ switch (mFormat) {
+ case static_cast<uint32_t>(PixelFormat4::RGBA_1010102): {
// TRICKY: this is used for media as YUV444 in the case when it is queued directly to a
// Surface. In all other cases it is RGBA. We don't know which case it is here, so
// default to YUV for now.
void *pointer = nullptr;
- if (mMapper2) {
- if (!mMapper2->lock(
- const_cast<native_handle_t *>(mBuffer),
- grallocUsage,
- { (int32_t)rect.left, (int32_t)rect.top,
- (int32_t)rect.width, (int32_t)rect.height },
- // TODO: fence
- hidl_handle(),
- [&err, &pointer](const auto &maperr, const auto &mapPointer) {
- err = maperr2error(maperr);
- if (err == C2_OK) {
- pointer = mapPointer;
- }
- }).isOk()) {
- ALOGE("failed transaction: lock(RGBA_1010102)");
- return C2_CORRUPTED;
- }
- } else {
- if (!mMapper3->lock(
- const_cast<native_handle_t *>(mBuffer),
- grallocUsage,
- { (int32_t)rect.left, (int32_t)rect.top,
- (int32_t)rect.width, (int32_t)rect.height },
- // TODO: fence
- hidl_handle(),
- [&err, &pointer](const auto &maperr, const auto &mapPointer,
- int32_t bytesPerPixel, int32_t bytesPerStride) {
- err = maperr2error(maperr);
- if (err == C2_OK) {
- pointer = mapPointer;
- }
- (void)bytesPerPixel;
- (void)bytesPerStride;
- }).isOk()) {
- ALOGE("failed transaction: lock(RGBA_1010102) (@3.0)");
- return C2_CORRUPTED;
- }
- }
- if (err != C2_OK) {
- ALOGD("lock failed: %d", err);
- return err;
+ // TODO: fence
+ status_t err = GraphicBufferMapper::get().lock(
+ const_cast<native_handle_t *>(mBuffer), grallocUsage,
+ { (int32_t)rect.left, (int32_t)rect.top,
+ (int32_t)rect.width, (int32_t)rect.height },
+ &pointer);
+ if (err) {
+ ALOGE("failed transaction: lock(RGBA_1010102)");
+ return C2_CORRUPTED;
}
// treat as 32-bit values
addr[C2PlanarLayout::PLANE_Y] = (uint8_t *)pointer;
@@ -533,13 +402,10 @@
layout->type = C2PlanarLayout::TYPE_YUVA;
layout->numPlanes = 4;
layout->rootPlanes = 1;
- int32_t stride = mMapper2 ?
- int32_t(mInfo2.stride) :
- int32_t(mInfo3.stride);
layout->planes[C2PlanarLayout::PLANE_Y] = {
C2PlaneInfo::CHANNEL_Y, // channel
4, // colInc
- 4 * stride, // rowInc
+ static_cast<int32_t>(4 * mStride), // rowInc
1, // mColSampling
1, // mRowSampling
32, // allocatedDepth
@@ -552,7 +418,7 @@
layout->planes[C2PlanarLayout::PLANE_U] = {
C2PlaneInfo::CHANNEL_CB, // channel
4, // colInc
- 4 * stride, // rowInc
+ static_cast<int32_t>(4 * mStride), // rowInc
1, // mColSampling
1, // mRowSampling
32, // allocatedDepth
@@ -565,7 +431,7 @@
layout->planes[C2PlanarLayout::PLANE_V] = {
C2PlaneInfo::CHANNEL_CR, // channel
4, // colInc
- 4 * stride, // rowInc
+ static_cast<int32_t>(4 * mStride), // rowInc
1, // mColSampling
1, // mRowSampling
32, // allocatedDepth
@@ -578,7 +444,7 @@
layout->planes[C2PlanarLayout::PLANE_A] = {
C2PlaneInfo::CHANNEL_A, // channel
4, // colInc
- 4 * stride, // rowInc
+ static_cast<int32_t>(4 * mStride), // rowInc
1, // mColSampling
1, // mRowSampling
32, // allocatedDepth
@@ -591,52 +457,20 @@
break;
}
- case PixelFormat3::RGBA_8888:
+ case static_cast<uint32_t>(PixelFormat4::RGBA_8888):
// TODO: alpha channel
// fall-through
- case PixelFormat3::RGBX_8888: {
+ case static_cast<uint32_t>(PixelFormat4::RGBX_8888): {
void *pointer = nullptr;
- if (mMapper2) {
- if (!mMapper2->lock(
- const_cast<native_handle_t *>(mBuffer),
- grallocUsage,
- { (int32_t)rect.left, (int32_t)rect.top,
- (int32_t)rect.width, (int32_t)rect.height },
- // TODO: fence
- hidl_handle(),
- [&err, &pointer](const auto &maperr, const auto &mapPointer) {
- err = maperr2error(maperr);
- if (err == C2_OK) {
- pointer = mapPointer;
- }
- }).isOk()) {
- ALOGE("failed transaction: lock(RGBA_8888)");
- return C2_CORRUPTED;
- }
- } else {
- if (!mMapper3->lock(
- const_cast<native_handle_t *>(mBuffer),
- grallocUsage,
- { (int32_t)rect.left, (int32_t)rect.top,
- (int32_t)rect.width, (int32_t)rect.height },
- // TODO: fence
- hidl_handle(),
- [&err, &pointer](const auto &maperr, const auto &mapPointer,
- int32_t bytesPerPixel, int32_t bytesPerStride) {
- err = maperr2error(maperr);
- if (err == C2_OK) {
- pointer = mapPointer;
- }
- (void)bytesPerPixel;
- (void)bytesPerStride;
- }).isOk()) {
- ALOGE("failed transaction: lock(RGBA_8888) (@3.0)");
- return C2_CORRUPTED;
- }
- }
- if (err != C2_OK) {
- ALOGD("lock failed: %d", err);
- return err;
+ // TODO: fence
+ status_t err = GraphicBufferMapper::get().lock(
+ const_cast<native_handle_t*>(mBuffer), grallocUsage,
+ { (int32_t)rect.left, (int32_t)rect.top,
+ (int32_t)rect.width, (int32_t)rect.height },
+ &pointer);
+ if (err) {
+ ALOGE("failed transaction: lock(RGBA_8888)");
+ return C2_CORRUPTED;
}
addr[C2PlanarLayout::PLANE_R] = (uint8_t *)pointer;
addr[C2PlanarLayout::PLANE_G] = (uint8_t *)pointer + 1;
@@ -644,13 +478,10 @@
layout->type = C2PlanarLayout::TYPE_RGB;
layout->numPlanes = 3;
layout->rootPlanes = 1;
- int32_t stride = mMapper2 ?
- int32_t(mInfo2.stride) :
- int32_t(mInfo3.stride);
layout->planes[C2PlanarLayout::PLANE_R] = {
C2PlaneInfo::CHANNEL_R, // channel
4, // colInc
- 4 * stride, // rowInc
+ static_cast<int32_t>(4 * mStride), // rowInc
1, // mColSampling
1, // mRowSampling
8, // allocatedDepth
@@ -663,7 +494,7 @@
layout->planes[C2PlanarLayout::PLANE_G] = {
C2PlaneInfo::CHANNEL_G, // channel
4, // colInc
- 4 * stride, // rowInc
+ static_cast<int32_t>(4 * mStride), // rowInc
1, // mColSampling
1, // mRowSampling
8, // allocatedDepth
@@ -676,7 +507,7 @@
layout->planes[C2PlanarLayout::PLANE_B] = {
C2PlaneInfo::CHANNEL_B, // channel
4, // colInc
- 4 * stride, // rowInc
+ static_cast<int32_t>(4 * mStride), // rowInc
1, // mColSampling
1, // mRowSampling
8, // allocatedDepth
@@ -689,69 +520,23 @@
break;
}
- case PixelFormat3::YCBCR_420_888:
+ case static_cast<uint32_t>(PixelFormat4::YCBCR_420_888):
// fall-through
- case PixelFormat3::YV12:
+ case static_cast<uint32_t>(PixelFormat4::YV12):
// fall-through
default: {
- struct YCbCrLayout {
- void* y;
- void* cb;
- void* cr;
- uint32_t yStride;
- uint32_t cStride;
- uint32_t chromaStep;
- };
- YCbCrLayout ycbcrLayout;
- if (mMapper2) {
- if (!mMapper2->lockYCbCr(
- const_cast<native_handle_t *>(mBuffer), grallocUsage,
+ android_ycbcr ycbcrLayout;
+
+ status_t err = GraphicBufferMapper::get().lockYCbCr(
+ const_cast<native_handle_t*>(mBuffer), grallocUsage,
{ (int32_t)rect.left, (int32_t)rect.top,
(int32_t)rect.width, (int32_t)rect.height },
- // TODO: fence
- hidl_handle(),
- [&err, &ycbcrLayout](const auto &maperr, const auto &mapLayout) {
- err = maperr2error(maperr);
- if (err == C2_OK) {
- ycbcrLayout = YCbCrLayout{
- mapLayout.y,
- mapLayout.cb,
- mapLayout.cr,
- mapLayout.yStride,
- mapLayout.cStride,
- mapLayout.chromaStep};
- }
- }).isOk()) {
- ALOGE("failed transaction: lockYCbCr");
- return C2_CORRUPTED;
- }
- } else {
- if (!mMapper3->lockYCbCr(
- const_cast<native_handle_t *>(mBuffer), grallocUsage,
- { (int32_t)rect.left, (int32_t)rect.top,
- (int32_t)rect.width, (int32_t)rect.height },
- // TODO: fence
- hidl_handle(),
- [&err, &ycbcrLayout](const auto &maperr, const auto &mapLayout) {
- err = maperr2error(maperr);
- if (err == C2_OK) {
- ycbcrLayout = YCbCrLayout{
- mapLayout.y,
- mapLayout.cb,
- mapLayout.cr,
- mapLayout.yStride,
- mapLayout.cStride,
- mapLayout.chromaStep};
- }
- }).isOk()) {
- ALOGE("failed transaction: lockYCbCr (@3.0)");
- return C2_CORRUPTED;
- }
+ &ycbcrLayout);
+ if (err) {
+ ALOGE("failed transaction: lockYCbCr");
+ return C2_CORRUPTED;
}
- if (err != C2_OK) {
- ALOGD("lockYCbCr failed: %d", err);
- return err;
- }
+
addr[C2PlanarLayout::PLANE_Y] = (uint8_t *)ycbcrLayout.y;
addr[C2PlanarLayout::PLANE_U] = (uint8_t *)ycbcrLayout.cb;
addr[C2PlanarLayout::PLANE_V] = (uint8_t *)ycbcrLayout.cr;
@@ -761,7 +546,7 @@
layout->planes[C2PlanarLayout::PLANE_Y] = {
C2PlaneInfo::CHANNEL_Y, // channel
1, // colInc
- (int32_t)ycbcrLayout.yStride, // rowInc
+ (int32_t)ycbcrLayout.ystride, // rowInc
1, // mColSampling
1, // mRowSampling
8, // allocatedDepth
@@ -773,8 +558,8 @@
};
layout->planes[C2PlanarLayout::PLANE_U] = {
C2PlaneInfo::CHANNEL_CB, // channel
- (int32_t)ycbcrLayout.chromaStep, // colInc
- (int32_t)ycbcrLayout.cStride, // rowInc
+ (int32_t)ycbcrLayout.chroma_step, // colInc
+ (int32_t)ycbcrLayout.cstride, // rowInc
2, // mColSampling
2, // mRowSampling
8, // allocatedDepth
@@ -786,8 +571,8 @@
};
layout->planes[C2PlanarLayout::PLANE_V] = {
C2PlaneInfo::CHANNEL_CR, // channel
- (int32_t)ycbcrLayout.chromaStep, // colInc
- (int32_t)ycbcrLayout.cStride, // rowInc
+ (int32_t)ycbcrLayout.chroma_step, // colInc
+ (int32_t)ycbcrLayout.cstride, // rowInc
2, // mColSampling
2, // mRowSampling
8, // allocatedDepth
@@ -799,11 +584,11 @@
};
// handle interleaved formats
intptr_t uvOffset = addr[C2PlanarLayout::PLANE_V] - addr[C2PlanarLayout::PLANE_U];
- if (uvOffset > 0 && uvOffset < (intptr_t)ycbcrLayout.chromaStep) {
+ if (uvOffset > 0 && uvOffset < (intptr_t)ycbcrLayout.chroma_step) {
layout->rootPlanes = 2;
layout->planes[C2PlanarLayout::PLANE_V].rootIx = C2PlanarLayout::PLANE_U;
layout->planes[C2PlanarLayout::PLANE_V].offset = uvOffset;
- } else if (uvOffset < 0 && uvOffset > -(intptr_t)ycbcrLayout.chromaStep) {
+ } else if (uvOffset < 0 && uvOffset > -(intptr_t)ycbcrLayout.chroma_step) {
layout->rootPlanes = 2;
layout->planes[C2PlanarLayout::PLANE_U].rootIx = C2PlanarLayout::PLANE_V;
layout->planes[C2PlanarLayout::PLANE_U].offset = -uvOffset;
@@ -821,44 +606,18 @@
// TODO: check addr and size, use fence
(void)addr;
(void)rect;
+ (void)fence;
std::lock_guard<std::mutex> lock(mMappedLock);
- c2_status_t err = C2_OK;
- if (mMapper2) {
- if (!mMapper2->unlock(
- const_cast<native_handle_t *>(mBuffer),
- [&err, &fence](const auto &maperr, const auto &releaseFence) {
- // TODO
- (void) fence;
- (void) releaseFence;
- err = maperr2error(maperr);
- if (err == C2_OK) {
- // TODO: fence
- }
- }).isOk()) {
- ALOGE("failed transaction: unlock");
- return C2_CORRUPTED;
- }
- } else {
- if (!mMapper3->unlock(
- const_cast<native_handle_t *>(mBuffer),
- [&err, &fence](const auto &maperr, const auto &releaseFence) {
- // TODO
- (void) fence;
- (void) releaseFence;
- err = maperr2error(maperr);
- if (err == C2_OK) {
- // TODO: fence
- }
- }).isOk()) {
- ALOGE("failed transaction: unlock (@3.0)");
- return C2_CORRUPTED;
- }
+ // TODO: fence
+ status_t err = GraphicBufferMapper::get().unlock(mBuffer);
+ if (err) {
+ ALOGE("failed transaction: unlock");
+ return C2_CORRUPTED;
}
- if (err == C2_OK) {
- mLocked = false;
- }
- return err;
+
+ mLocked = false;
+ return C2_OK;
}
bool C2AllocationGralloc::equals(const std::shared_ptr<const C2GraphicAllocation> &other) const {
@@ -895,10 +654,6 @@
private:
std::shared_ptr<C2Allocator::Traits> mTraits;
c2_status_t mInit;
- sp<IAllocator2> mAllocator2;
- sp<IMapper2> mMapper2;
- sp<IAllocator3> mAllocator3;
- sp<IMapper3> mMapper3;
const bool mBufferQueue;
};
@@ -916,21 +671,6 @@
C2MemoryUsage minUsage = { 0, 0 }, maxUsage = { ~(uint64_t)0, ~(uint64_t)0 };
Traits traits = { "android.allocator.gralloc", id, C2Allocator::GRAPHIC, minUsage, maxUsage };
mTraits = std::make_shared<C2Allocator::Traits>(traits);
-
- // gralloc allocator is a singleton, so all objects share a global service
- mAllocator3 = IAllocator3::getService();
- mMapper3 = IMapper3::getService();
- if (!mAllocator3 || !mMapper3) {
- mAllocator3 = nullptr;
- mMapper3 = nullptr;
- mAllocator2 = IAllocator2::getService();
- mMapper2 = IMapper2::getService();
- if (!mAllocator2 || !mMapper2) {
- mAllocator2 = nullptr;
- mMapper2 = nullptr;
- mInit = C2_CORRUPTED;
- }
- }
}
c2_status_t C2AllocatorGralloc::Impl::newGraphicAllocation(
@@ -940,176 +680,59 @@
ALOGV("allocating buffer with usage %#llx => %#llx",
(long long)usage.expected, (long long)grallocUsage);
- c2_status_t err = C2_OK;
- hidl_handle buffer{};
+ buffer_handle_t buffer;
- if (mMapper2) {
- BufferDescriptorInfo2 info = {
- {
- width,
- height,
- 1u, // layerCount
- PixelFormat2(format),
- grallocUsage,
- },
- 0u, // stride placeholder
- };
- BufferDescriptor2 desc;
- if (!mMapper2->createDescriptor(
- info.mapperInfo, [&err, &desc](const auto &maperr, const auto &descriptor) {
- err = maperr2error(maperr);
- if (err == C2_OK) {
- desc = descriptor;
- }
- }).isOk()) {
- ALOGE("failed transaction: createDescriptor");
- return C2_CORRUPTED;
- }
- if (err != C2_OK) {
- return err;
- }
+ uint32_t stride = 0;
- // IAllocator shares IMapper error codes.
- if (!mAllocator2->allocate(
- desc,
- 1u,
- [&err, &buffer, &info](const auto &maperr, const auto &stride, auto &buffers) {
- err = maperr2error(maperr);
- if (err != C2_OK) {
- return;
- }
- if (buffers.size() != 1u) {
- err = C2_CORRUPTED;
- return;
- }
- info.stride = stride;
- buffer = buffers[0];
- }).isOk()) {
- ALOGE("failed transaction: allocate");
- return C2_CORRUPTED;
- }
- if (err != C2_OK) {
- return err;
- }
- allocation->reset(new C2AllocationGralloc(
- info, mMapper2, buffer,
- C2HandleGralloc::WrapAndMoveNativeHandle(
- buffer.getNativeHandle(),
- width, height,
- format, grallocUsage, info.stride,
- 0, 0, mBufferQueue ? ~0 : 0),
- mTraits->id));
- return C2_OK;
- } else {
- BufferDescriptorInfo3 info = {
- {
- width,
- height,
- 1u, // layerCount
- PixelFormat3(format),
- grallocUsage,
- },
- 0u, // stride placeholder
- };
- BufferDescriptor3 desc;
- if (!mMapper3->createDescriptor(
- info.mapperInfo, [&err, &desc](const auto &maperr, const auto &descriptor) {
- err = maperr2error(maperr);
- if (err == C2_OK) {
- desc = descriptor;
- }
- }).isOk()) {
- ALOGE("failed transaction: createDescriptor");
- return C2_CORRUPTED;
- }
- if (err != C2_OK) {
- return err;
- }
-
- // IAllocator shares IMapper error codes.
- if (!mAllocator3->allocate(
- desc,
- 1u,
- [&err, &buffer, &info](const auto &maperr, const auto &stride, auto &buffers) {
- err = maperr2error(maperr);
- if (err != C2_OK) {
- return;
- }
- if (buffers.size() != 1u) {
- err = C2_CORRUPTED;
- return;
- }
- info.stride = stride;
- buffer = buffers[0];
- }).isOk()) {
- ALOGE("failed transaction: allocate");
- return C2_CORRUPTED;
- }
- if (err != C2_OK) {
- return err;
- }
- allocation->reset(new C2AllocationGralloc(
- info, mMapper3, buffer,
- C2HandleGralloc::WrapAndMoveNativeHandle(
- buffer.getNativeHandle(),
- width, height,
- format, grallocUsage, info.stride,
- 0, 0, mBufferQueue ? ~0 : 0),
- mTraits->id));
- return C2_OK;
+ status_t err = GraphicBufferAllocator::get().allocateRawHandle(width, height, format,
+ 1u /* layer count */, grallocUsage, &buffer, &stride, "C2GrallocAllocation");
+ if (err) {
+ ALOGE("failed transaction: allocate");
+ return C2_CORRUPTED;
}
+
+ hidl_handle hidlHandle;
+ hidlHandle.setTo(const_cast<native_handle_t*>(buffer), true);
+
+ allocation->reset(new C2AllocationGralloc(
+ width, height, format, 1u /* layer count */, grallocUsage, stride, hidlHandle,
+ C2HandleGralloc::WrapAndMoveNativeHandle(
+ hidlHandle, width, height,
+ format, grallocUsage, stride,
+ 0, 0, mBufferQueue ? ~0 : 0),
+ mTraits->id));
+ return C2_OK;
}
c2_status_t C2AllocatorGralloc::Impl::priorGraphicAllocation(
const C2Handle *handle,
std::shared_ptr<C2GraphicAllocation> *allocation) {
- if (mMapper2) {
- BufferDescriptorInfo2 info;
- info.mapperInfo.layerCount = 1u;
- uint32_t generation;
- uint64_t igbp_id;
- uint32_t igbp_slot;
- const C2HandleGralloc *grallocHandle = C2HandleGralloc::Import(
- handle,
- &info.mapperInfo.width, &info.mapperInfo.height,
- (uint32_t *)&info.mapperInfo.format,
- (uint64_t *)&info.mapperInfo.usage,
- &info.stride,
- &generation, &igbp_id, &igbp_slot);
- if (grallocHandle == nullptr) {
- return C2_BAD_VALUE;
- }
- hidl_handle hidlHandle;
- hidlHandle.setTo(C2HandleGralloc::UnwrapNativeHandle(grallocHandle), true);
+ uint32_t generation;
+ uint64_t igbp_id;
+ uint32_t igbp_slot;
- allocation->reset(new C2AllocationGralloc(
- info, mMapper2, hidlHandle, grallocHandle, mTraits->id));
- return C2_OK;
- } else {
- BufferDescriptorInfo3 info;
- info.mapperInfo.layerCount = 1u;
- uint32_t generation;
- uint64_t igbp_id;
- uint32_t igbp_slot;
- const C2HandleGralloc *grallocHandle = C2HandleGralloc::Import(
- handle,
- &info.mapperInfo.width, &info.mapperInfo.height,
- (uint32_t *)&info.mapperInfo.format,
- (uint64_t *)&info.mapperInfo.usage,
- &info.stride,
- &generation, &igbp_id, &igbp_slot);
- if (grallocHandle == nullptr) {
- return C2_BAD_VALUE;
- }
+ uint32_t width;
+ uint32_t height;
+ uint32_t format;
+ uint32_t layerCount = 1;
+ uint64_t grallocUsage;
+ uint32_t stride;
- hidl_handle hidlHandle;
- hidlHandle.setTo(C2HandleGralloc::UnwrapNativeHandle(grallocHandle), true);
-
- allocation->reset(new C2AllocationGralloc(
- info, mMapper3, hidlHandle, grallocHandle, mTraits->id));
- return C2_OK;
+ const C2HandleGralloc *grallocHandle = C2HandleGralloc::Import(
+ handle, &width, &height, &format, &grallocUsage, &stride,
+ &generation, &igbp_id, &igbp_slot);
+ if (grallocHandle == nullptr) {
+ return C2_BAD_VALUE;
}
+
+ hidl_handle hidlHandle;
+ hidlHandle.setTo(C2HandleGralloc::UnwrapNativeHandle(grallocHandle), true);
+
+ allocation->reset(new C2AllocationGralloc(
+ width, height, format, layerCount,
+ grallocUsage, stride, hidlHandle, grallocHandle, mTraits->id));
+ return C2_OK;
}
C2AllocatorGralloc::C2AllocatorGralloc(id_t id, bool bufferQueue)
diff --git a/media/codec2/vndk/C2Buffer.cpp b/media/codec2/vndk/C2Buffer.cpp
index 2d99b53..0b08f31 100644
--- a/media/codec2/vndk/C2Buffer.cpp
+++ b/media/codec2/vndk/C2Buffer.cpp
@@ -22,14 +22,17 @@
#include <map>
#include <mutex>
-#include <C2AllocatorIon.h>
+#include <C2AllocatorBlob.h>
#include <C2AllocatorGralloc.h>
+#include <C2AllocatorIon.h>
#include <C2BufferPriv.h>
#include <C2BlockInternal.h>
+#include <C2PlatformSupport.h>
#include <bufferpool/ClientManager.h>
namespace {
+using android::C2AllocatorBlob;
using android::C2AllocatorGralloc;
using android::C2AllocatorIon;
using android::hardware::media::bufferpool::BufferPoolData;
@@ -393,10 +396,29 @@
std::shared_ptr<C2LinearBlock> _C2BlockFactory::CreateLinearBlock(
const C2Handle *handle) {
// TODO: get proper allocator? and mutex?
- static std::unique_ptr<C2AllocatorIon> sAllocator = std::make_unique<C2AllocatorIon>(0);
+ static std::unique_ptr<C2Allocator> sAllocator = []{
+ std::unique_ptr<C2Allocator> allocator;
+ if (android::GetPreferredLinearAllocatorId(android::GetCodec2PoolMask()) ==
+ android::C2PlatformAllocatorStore::BLOB) {
+ allocator = std::make_unique<C2AllocatorBlob>(android::C2PlatformAllocatorStore::BLOB);
+ } else {
+ allocator = std::make_unique<C2AllocatorIon>(android::C2PlatformAllocatorStore::ION);
+ }
+ return allocator;
+ }();
+
+ if (sAllocator == nullptr)
+ return nullptr;
+
+ bool isValidHandle = false;
+ if (sAllocator->getId() == android::C2PlatformAllocatorStore::BLOB) {
+ isValidHandle = C2AllocatorBlob::isValid(handle);
+ } else {
+ isValidHandle = C2AllocatorIon::isValid(handle);
+ }
std::shared_ptr<C2LinearAllocation> alloc;
- if (C2AllocatorIon::isValid(handle)) {
+ if (isValidHandle) {
c2_status_t err = sAllocator->priorLinearAllocation(handle, &alloc);
if (err == C2_OK) {
std::shared_ptr<C2LinearBlock> block = _C2BlockFactory::CreateLinearBlock(alloc);
@@ -409,10 +431,29 @@
std::shared_ptr<C2LinearBlock> _C2BlockFactory::CreateLinearBlock(
const C2Handle *cHandle, const std::shared_ptr<BufferPoolData> &data) {
// TODO: get proper allocator? and mutex?
- static std::unique_ptr<C2AllocatorIon> sAllocator = std::make_unique<C2AllocatorIon>(0);
+ static std::unique_ptr<C2Allocator> sAllocator = []{
+ std::unique_ptr<C2Allocator> allocator;
+ if (android::GetPreferredLinearAllocatorId(android::GetCodec2PoolMask()) ==
+ android::C2PlatformAllocatorStore::BLOB) {
+ allocator = std::make_unique<C2AllocatorBlob>(android::C2PlatformAllocatorStore::BLOB);
+ } else {
+ allocator = std::make_unique<C2AllocatorIon>(android::C2PlatformAllocatorStore::ION);
+ }
+ return allocator;
+ }();
+
+ if (sAllocator == nullptr)
+ return nullptr;
+
+ bool isValidHandle = false;
+ if (sAllocator->getId() == android::C2PlatformAllocatorStore::BLOB) {
+ isValidHandle = C2AllocatorBlob::isValid(cHandle);
+ } else {
+ isValidHandle = C2AllocatorIon::isValid(cHandle);
+ }
std::shared_ptr<C2LinearAllocation> alloc;
- if (C2AllocatorIon::isValid(cHandle)) {
+ if (isValidHandle) {
c2_status_t err = sAllocator->priorLinearAllocation(cHandle, &alloc);
const std::shared_ptr<C2PooledBlockPoolData> poolData =
std::make_shared<C2PooledBlockPoolData>(data);
diff --git a/media/codec2/vndk/C2Store.cpp b/media/codec2/vndk/C2Store.cpp
index e0408b7..d16527e 100644
--- a/media/codec2/vndk/C2Store.cpp
+++ b/media/codec2/vndk/C2Store.cpp
@@ -18,6 +18,7 @@
#define LOG_NDEBUG 0
#include <utils/Log.h>
+#include <C2AllocatorBlob.h>
#include <C2AllocatorGralloc.h>
#include <C2AllocatorIon.h>
#include <C2BufferPriv.h>
@@ -26,6 +27,7 @@
#include <C2Config.h>
#include <C2PlatformStorePluginLoader.h>
#include <C2PlatformSupport.h>
+#include <cutils/properties.h>
#include <util/C2InterfaceHelper.h>
#include <dlfcn.h>
@@ -75,6 +77,9 @@
~C2PlatformAllocatorStoreImpl() override = default;
private:
+ /// returns a shared-singleton blob allocator (gralloc-backed)
+ std::shared_ptr<C2Allocator> fetchBlobAllocator();
+
/// returns a shared-singleton ion allocator
std::shared_ptr<C2Allocator> fetchIonAllocator();
@@ -97,10 +102,12 @@
c2_status_t C2PlatformAllocatorStoreImpl::fetchAllocator(
id_t id, std::shared_ptr<C2Allocator> *const allocator) {
allocator->reset();
+ if (id == C2AllocatorStore::DEFAULT_LINEAR) {
+ id = GetPreferredLinearAllocatorId(GetCodec2PoolMask());
+ }
switch (id) {
// TODO: should we implement a generic registry for all, and use that?
case C2PlatformAllocatorStore::ION:
- case C2AllocatorStore::DEFAULT_LINEAR:
*allocator = fetchIonAllocator();
break;
@@ -113,6 +120,10 @@
*allocator = fetchBufferQueueAllocator();
break;
+ case C2PlatformAllocatorStore::BLOB:
+ *allocator = fetchBlobAllocator();
+ break;
+
default:
// Try to create allocator from platform store plugins.
c2_status_t res =
@@ -222,6 +233,18 @@
return allocator;
}
+std::shared_ptr<C2Allocator> C2PlatformAllocatorStoreImpl::fetchBlobAllocator() {
+ static std::mutex mutex;
+ static std::weak_ptr<C2Allocator> blobAllocator;
+ std::lock_guard<std::mutex> lock(mutex);
+ std::shared_ptr<C2Allocator> allocator = blobAllocator.lock();
+ if (allocator == nullptr) {
+ allocator = std::make_shared<C2AllocatorBlob>(C2PlatformAllocatorStore::BLOB);
+ blobAllocator = allocator;
+ }
+ return allocator;
+}
+
std::shared_ptr<C2Allocator> C2PlatformAllocatorStoreImpl::fetchGrallocAllocator() {
static std::mutex mutex;
static std::weak_ptr<C2Allocator> grallocAllocator;
@@ -292,6 +315,18 @@
return gPreferredComponentStore ? gPreferredComponentStore : GetCodec2PlatformComponentStore();
}
+int GetCodec2PoolMask() {
+ return property_get_int32(
+ "debug.stagefright.c2-poolmask",
+ 1 << C2PlatformAllocatorStore::ION |
+ 1 << C2PlatformAllocatorStore::BUFFERQUEUE);
+}
+
+C2PlatformAllocatorStore::id_t GetPreferredLinearAllocatorId(int poolMask) {
+ return ((poolMask >> C2PlatformAllocatorStore::BLOB) & 1) ? C2PlatformAllocatorStore::BLOB
+ : C2PlatformAllocatorStore::ION;
+}
+
namespace {
class _C2BlockPoolCache {
@@ -308,11 +343,25 @@
std::shared_ptr<C2Allocator> allocator;
c2_status_t res = C2_NOT_FOUND;
+ if (allocatorId == C2AllocatorStore::DEFAULT_LINEAR) {
+ allocatorId = GetPreferredLinearAllocatorId(GetCodec2PoolMask());
+ }
switch(allocatorId) {
case C2PlatformAllocatorStore::ION:
- case C2AllocatorStore::DEFAULT_LINEAR:
res = allocatorStore->fetchAllocator(
- C2AllocatorStore::DEFAULT_LINEAR, &allocator);
+ C2PlatformAllocatorStore::ION, &allocator);
+ if (res == C2_OK) {
+ std::shared_ptr<C2BlockPool> ptr =
+ std::make_shared<C2PooledBlockPool>(
+ allocator, poolId);
+ *pool = ptr;
+ mBlockPools[poolId] = ptr;
+ mComponents[poolId] = component;
+ }
+ break;
+ case C2PlatformAllocatorStore::BLOB:
+ res = allocatorStore->fetchAllocator(
+ C2PlatformAllocatorStore::BLOB, &allocator);
if (res == C2_OK) {
std::shared_ptr<C2BlockPool> ptr =
std::make_shared<C2PooledBlockPool>(
diff --git a/media/codec2/vndk/include/C2AllocatorBlob.h b/media/codec2/vndk/include/C2AllocatorBlob.h
new file mode 100644
index 0000000..89ce949
--- /dev/null
+++ b/media/codec2/vndk/include/C2AllocatorBlob.h
@@ -0,0 +1,57 @@
+/*
+ * 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 STAGEFRIGHT_CODEC2_ALLOCATOR_BLOB_H_
+#define STAGEFRIGHT_CODEC2_ALLOCATOR_BLOB_H_
+
+#include <functional>
+
+#include <C2AllocatorGralloc.h>
+#include <C2Buffer.h>
+
+namespace android {
+
+class C2AllocatorBlob : public C2Allocator {
+public:
+ virtual id_t getId() const override;
+
+ virtual C2String getName() const override;
+
+ virtual std::shared_ptr<const Traits> getTraits() const override;
+
+ virtual c2_status_t newLinearAllocation(
+ uint32_t capacity, C2MemoryUsage usage,
+ std::shared_ptr<C2LinearAllocation> *allocation) override;
+
+ virtual c2_status_t priorLinearAllocation(
+ const C2Handle *handle,
+ std::shared_ptr<C2LinearAllocation> *allocation) override;
+
+ C2AllocatorBlob(id_t id);
+
+ virtual ~C2AllocatorBlob() override;
+
+ static bool isValid(const C2Handle* const o);
+
+private:
+ std::shared_ptr<const Traits> mTraits;
+ // Design as C2AllocatorGralloc-backed to unify Gralloc implementations.
+ std::shared_ptr<C2Allocator> mC2AllocatorGralloc;
+};
+
+} // namespace android
+
+#endif // STAGEFRIGHT_CODEC2_ALLOCATOR_BLOB_H_
diff --git a/media/codec2/vndk/include/C2PlatformSupport.h b/media/codec2/vndk/include/C2PlatformSupport.h
index f31282c..a14e0d3 100644
--- a/media/codec2/vndk/include/C2PlatformSupport.h
+++ b/media/codec2/vndk/include/C2PlatformSupport.h
@@ -66,6 +66,15 @@
BUFFERQUEUE,
/**
+ * ID of the gralloc backed platform allocator for linear blob buffer.
+ *
+ * C2Handle layout is not public. Use C2AllocatorGralloc::UnwrapNativeCodec2GrallocHandle
+ * to get the underlying gralloc handle from a C2Handle, and WrapNativeCodec2GrallocHandle
+ * to create a C2Handle from a gralloc handle - for C2Allocator::priorAllocation.
+ */
+ BLOB,
+
+ /**
* ID of indicating the end of platform allocator definition.
*
* \note always put this macro in the last place.
@@ -131,6 +140,20 @@
*/
void SetPreferredCodec2ComponentStore(std::shared_ptr<C2ComponentStore> store);
+/**
+ * Returns the pool mask.
+ * \retval the default pool mask should be adopted if it could not be obtained from property
+ * "debug.stagefright.c2-poolmask"
+ */
+int GetCodec2PoolMask();
+
+/**
+ * Returns the preferred linear buffer allocator id from param poolMask.
+ * C2PlatformAllocatorStore::ION should be chosen as fallback allocator if BLOB is not enabled from
+ * param poolMask.
+ */
+C2PlatformAllocatorStore::id_t GetPreferredLinearAllocatorId(int poolMask);
+
} // namespace android
#endif // STAGEFRIGHT_CODEC2_PLATFORM_SUPPORT_H_
diff --git a/media/codec2/vndk/platform/C2BqBuffer.cpp b/media/codec2/vndk/platform/C2BqBuffer.cpp
index 8304f74..62936f6 100644
--- a/media/codec2/vndk/platform/C2BqBuffer.cpp
+++ b/media/codec2/vndk/platform/C2BqBuffer.cpp
@@ -576,10 +576,11 @@
}
}
int migrated = 0;
+ // poolDatas dtor should not be called during lock is held.
+ std::shared_ptr<C2BufferQueueBlockPoolData>
+ poolDatas[NUM_BUFFER_SLOTS];
{
sp<GraphicBuffer> buffers[NUM_BUFFER_SLOTS];
- std::weak_ptr<C2BufferQueueBlockPoolData>
- poolDatas[NUM_BUFFER_SLOTS];
std::scoped_lock<std::mutex> lock(mMutex);
bool noInit = false;
for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) {
diff --git a/media/extractors/mkv/Android.bp b/media/extractors/mkv/Android.bp
index 650d79d..942c88a 100644
--- a/media/extractors/mkv/Android.bp
+++ b/media/extractors/mkv/Android.bp
@@ -12,10 +12,10 @@
shared_libs: [
"liblog",
"libmediandk",
+ "libstagefright_flacdec",
],
static_libs: [
- "libstagefright_flacdec",
"libstagefright_foundation",
"libstagefright_metadatautils",
"libwebm",
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/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index 86ed610..9e0bc96 100755
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -33,7 +33,7 @@
#include "ItemTable.h"
#include "include/ESDS.h"
-#include <media/DataSourceBase.h>
+#include <media/stagefright/DataSourceBase.h>
#include <media/ExtractorUtils.h>
#include <media/stagefright/foundation/ABitReader.h>
#include <media/stagefright/foundation/ABuffer.h>
@@ -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();
@@ -133,6 +134,7 @@
bool mIsAVC;
bool mIsHEVC;
+ bool mIsDolbyVision;
bool mIsAC4;
bool mIsPcm;
size_t mNALLengthSize;
@@ -150,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);
@@ -337,6 +340,14 @@
case FOURCC("hvc1"):
case FOURCC("hev1"):
return MEDIA_MIMETYPE_VIDEO_HEVC;
+
+ case FOURCC("dvav"):
+ case FOURCC("dva1"):
+ case FOURCC("dvhe"):
+ case FOURCC("dvh1"):
+ case FOURCC("dav1"):
+ return MEDIA_MIMETYPE_VIDEO_DOLBY_VISION;
+
case FOURCC("ac-4"):
return MEDIA_MIMETYPE_AUDIO_AC4;
case FOURCC("Opus"):
@@ -475,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;
@@ -1062,6 +1072,68 @@
mLastTrack->mTx3gBuffer = NULL;
}
+ const char *mime;
+ AMediaFormat_getString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, &mime);
+
+ if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION)) {
+ void *data;
+ size_t size;
+
+ if (AMediaFormat_getBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_2, &data, &size)) {
+ const uint8_t *ptr = (const uint8_t *)data;
+ const uint8_t profile = ptr[2] >> 1;
+ const uint8_t bl_compatibility_id = (ptr[4]) >> 4;
+ bool create_two_tracks = false;
+
+ if (bl_compatibility_id && bl_compatibility_id != 15) {
+ create_two_tracks = true;
+ }
+
+ if (4 == profile || 7 == profile ||
+ (profile >= 8 && profile < 11 && create_two_tracks)) {
+ // we need a backward compatible track
+ ALOGV("Adding new backward compatible track");
+ Track *track_b = new Track;
+
+ track_b->timescale = mLastTrack->timescale;
+ track_b->sampleTable = mLastTrack->sampleTable;
+ track_b->includes_expensive_metadata = mLastTrack->includes_expensive_metadata;
+ track_b->skipTrack = mLastTrack->skipTrack;
+ 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->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;
+ track_b->mTx3gSize = mLastTrack->mTx3gSize;
+ track_b->mTx3gFilled = mLastTrack->mTx3gFilled;
+
+ track_b->meta = AMediaFormat_new();
+ AMediaFormat_copy(track_b->meta, mLastTrack->meta);
+
+ mLastTrack->next = track_b;
+ track_b->next = NULL;
+
+ auto id = track_b->meta->mFormat->findEntryByName(AMEDIAFORMAT_KEY_CSD_2);
+ track_b->meta->mFormat->removeEntryAt(id);
+
+ if (4 == profile || 7 == profile || 8 == profile ) {
+ AMediaFormat_setString(track_b->meta,
+ AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_HEVC);
+ } else if (9 == profile) {
+ AMediaFormat_setString(track_b->meta,
+ AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_AVC);
+ } else if (10 == profile) {
+ AMediaFormat_setString(track_b->meta,
+ AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_AV1);
+ } // Should never get to else part
+
+ mLastTrack = track_b;
+ }
+ }
+ }
} else if (chunk_type == FOURCC("moov")) {
mInitCheck = OK;
@@ -1134,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;
}
@@ -1848,6 +1967,11 @@
case FOURCC("avc1"):
case FOURCC("hvc1"):
case FOURCC("hev1"):
+ case FOURCC("dvav"):
+ case FOURCC("dva1"):
+ case FOURCC("dvhe"):
+ case FOURCC("dvh1"):
+ case FOURCC("dav1"):
case FOURCC("av01"):
{
uint8_t buffer[78];
@@ -2002,7 +2126,8 @@
// for audio, use 128KB
max_size = 1024 * 128;
} else if (!strcmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)
- || !strcmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC)) {
+ || !strcmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC)
+ || !strcmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION)) {
// AVC & HEVC requires compression ratio of at least 2, and uses
// macroblocks
max_size = ((width + 15) / 16) * ((height + 15) / 16) * 192;
@@ -2333,6 +2458,33 @@
*offset += chunk_size;
break;
}
+ case FOURCC("dvcC"):
+ case FOURCC("dvvC"): {
+
+ CHECK_EQ(chunk_data_size, 24);
+
+ auto buffer = heapbuffer<uint8_t>(chunk_data_size);
+
+ if (buffer.get() == NULL) {
+ ALOGE("b/28471206");
+ return NO_MEMORY;
+ }
+
+ if (mDataSource->readAt(data_offset, buffer.get(), chunk_data_size) < chunk_data_size) {
+ return ERROR_IO;
+ }
+
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
+ AMediaFormat_setBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_2,
+ buffer.get(), chunk_data_size);
+ AMediaFormat_setString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME,
+ MEDIA_MIMETYPE_VIDEO_DOLBY_VISION);
+
+ *offset += chunk_size;
+ break;
+ }
case FOURCC("d263"):
{
*offset += chunk_size;
@@ -4145,7 +4297,20 @@
if (!strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC)) {
itemTable = mItemTable;
}
- } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1)) {
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION)) {
+ void *data;
+ size_t size;
+ if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_2, &data, &size)) {
+ return NULL;
+ }
+
+ const uint8_t *ptr = (const uint8_t *)data;
+
+ // dv_major.dv_minor Should be 1.0 or 2.1
+ if (size != 24 || ((ptr[0] != 1 || ptr[1] != 0) && (ptr[0] != 2 || ptr[1] != 1))) {
+ return NULL;
+ }
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1)) {
void *data;
size_t size;
if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_0, &data, &size)) {
@@ -4159,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;
@@ -4190,6 +4368,10 @@
if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_HEVC, &data, &size)) {
return ERROR_MALFORMED;
}
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION)) {
+ if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_2, &data, &size)) {
+ return ERROR_MALFORMED;
+ }
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1)) {
if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_0, &data, &size)) {
return ERROR_MALFORMED;
@@ -4656,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),
@@ -4677,6 +4860,7 @@
mCurrentSampleInfoOffsets(NULL),
mIsAVC(false),
mIsHEVC(false),
+ mIsDolbyVision(false),
mIsAC4(false),
mIsPcm(false),
mNALLengthSize(0),
@@ -4685,7 +4869,8 @@
mSrcBuffer(NULL),
mIsHeif(itemTable != NULL),
mItemTable(itemTable),
- mElstShiftStartTicks(elstShiftStartTicks) {
+ mElstShiftStartTicks(elstShiftStartTicks),
+ mElstInitialEmptyEditTicks(elstInitialEmptyEditTicks) {
memset(&mTrackFragmentHeaderInfo, 0, sizeof(mTrackFragmentHeaderInfo));
@@ -4716,6 +4901,7 @@
mIsHEVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC) ||
!strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
mIsAC4 = !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AC4);
+ mIsDolbyVision = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION);
if (mIsAVC) {
void *data;
@@ -4740,6 +4926,42 @@
CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1
mNALLengthSize = 1 + (ptr[14 + 7] & 3);
+ } else if (mIsDolbyVision) {
+ ALOGV("%s DolbyVision stream detected", __FUNCTION__);
+ void *data;
+ size_t size;
+ CHECK(AMediaFormat_getBuffer(format, AMEDIAFORMAT_KEY_CSD_2, &data, &size));
+
+ const uint8_t *ptr = (const uint8_t *)data;
+
+ CHECK(size == 24);
+
+ // dv_major.dv_minor Should be 1.0 or 2.1
+ CHECK(!((ptr[0] != 1 || ptr[1] != 0) && (ptr[0] != 2 || ptr[1] != 1)));
+
+ const uint8_t profile = ptr[2] >> 1;
+ // profile == (unknown,1,9) --> AVC; profile = (2,3,4,5,6,7,8) --> HEVC;
+ // profile == (10) --> AV1
+ if (profile > 1 && profile < 9) {
+ CHECK(AMediaFormat_getBuffer(format, AMEDIAFORMAT_KEY_CSD_HEVC, &data, &size));
+
+ const uint8_t *ptr = (const uint8_t *)data;
+
+ CHECK(size >= 22);
+ CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1
+
+ mNALLengthSize = 1 + (ptr[14 + 7] & 3);
+ } else if (10 == profile) {
+ /* AV1 profile nothing to do */
+ } else {
+ CHECK(AMediaFormat_getBuffer(format, AMEDIAFORMAT_KEY_CSD_AVC, &data, &size));
+ const uint8_t *ptr = (const uint8_t *)data;
+
+ CHECK(size >= 7);
+ CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1
+ // The number of bytes used to encode the length of a NAL unit.
+ mNALLengthSize = 1 + (ptr[4] & 3);
+ }
}
mIsPcm = !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW);
@@ -4767,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() {
@@ -5011,8 +5212,11 @@
}
status_t MPEG4Source::parseSampleAuxiliaryInformationSizes(
- off64_t offset, off64_t /* size */) {
+ off64_t offset, off64_t size) {
ALOGV("parseSampleAuxiliaryInformationSizes");
+ if (size < 9) {
+ return -EINVAL;
+ }
// 14496-12 8.7.12
uint8_t version;
if (mDataSource->readAt(
@@ -5025,25 +5229,32 @@
return ERROR_UNSUPPORTED;
}
offset++;
+ size--;
uint32_t flags;
if (!mDataSource->getUInt24(offset, &flags)) {
return ERROR_IO;
}
offset += 3;
+ size -= 3;
if (flags & 1) {
+ if (size < 13) {
+ return -EINVAL;
+ }
uint32_t tmp;
if (!mDataSource->getUInt32(offset, &tmp)) {
return ERROR_MALFORMED;
}
mCurrentAuxInfoType = tmp;
offset += 4;
+ size -= 4;
if (!mDataSource->getUInt32(offset, &tmp)) {
return ERROR_MALFORMED;
}
mCurrentAuxInfoTypeParameter = tmp;
offset += 4;
+ size -= 4;
}
uint8_t defsize;
@@ -5052,6 +5263,7 @@
}
mCurrentDefaultSampleInfoSize = defsize;
offset++;
+ size--;
uint32_t smplcnt;
if (!mDataSource->getUInt32(offset, &smplcnt)) {
@@ -5059,11 +5271,16 @@
}
mCurrentSampleInfoCount = smplcnt;
offset += 4;
-
+ size -= 4;
if (mCurrentDefaultSampleInfoSize != 0) {
ALOGV("@@@@ using default sample info size of %d", mCurrentDefaultSampleInfoSize);
return OK;
}
+ if(smplcnt > size) {
+ ALOGW("b/124525515 - smplcnt(%u) > size(%ld)", (unsigned int)smplcnt, (unsigned long)size);
+ android_errorWriteLog(0x534e4554, "124525515");
+ return -EINVAL;
+ }
if (smplcnt > mCurrentSampleInfoAllocSize) {
uint8_t * newPtr = (uint8_t*) realloc(mCurrentSampleInfoSizes, smplcnt);
if (newPtr == NULL) {
@@ -5079,26 +5296,32 @@
}
status_t MPEG4Source::parseSampleAuxiliaryInformationOffsets(
- off64_t offset, off64_t /* size */) {
+ off64_t offset, off64_t size) {
ALOGV("parseSampleAuxiliaryInformationOffsets");
+ if (size < 8) {
+ return -EINVAL;
+ }
// 14496-12 8.7.13
uint8_t version;
if (mDataSource->readAt(offset, &version, sizeof(version)) != 1) {
return ERROR_IO;
}
offset++;
+ size--;
uint32_t flags;
if (!mDataSource->getUInt24(offset, &flags)) {
return ERROR_IO;
}
offset += 3;
+ size -= 3;
uint32_t entrycount;
if (!mDataSource->getUInt32(offset, &entrycount)) {
return ERROR_IO;
}
offset += 4;
+ size -= 4;
if (entrycount == 0) {
return OK;
}
@@ -5124,19 +5347,31 @@
for (size_t i = 0; i < entrycount; i++) {
if (version == 0) {
+ if (size < 4) {
+ ALOGW("b/124526959");
+ android_errorWriteLog(0x534e4554, "124526959");
+ return -EINVAL;
+ }
uint32_t tmp;
if (!mDataSource->getUInt32(offset, &tmp)) {
return ERROR_IO;
}
mCurrentSampleInfoOffsets[i] = tmp;
offset += 4;
+ size -= 4;
} else {
+ if (size < 8) {
+ ALOGW("b/124526959");
+ android_errorWriteLog(0x534e4554, "124526959");
+ return -EINVAL;
+ }
uint64_t tmp;
if (!mDataSource->getUInt64(offset, &tmp)) {
return ERROR_IO;
}
mCurrentSampleInfoOffsets[i] = tmp;
offset += 8;
+ size -= 8;
}
}
@@ -5423,20 +5658,30 @@
if (flags & kSampleSizePresent) {
bytesPerSample += 4;
- } else if (mTrackFragmentHeaderInfo.mFlags
- & TrackFragmentHeaderInfo::kDefaultSampleSizePresent) {
- sampleSize = mTrackFragmentHeaderInfo.mDefaultSampleSize;
} else {
sampleSize = mTrackFragmentHeaderInfo.mDefaultSampleSize;
+#ifdef VERY_VERY_VERBOSE_LOGGING
+ // We don't expect this, but also want to avoid spamming the log if
+ // we hit this case.
+ if (!(mTrackFragmentHeaderInfo.mFlags
+ & TrackFragmentHeaderInfo::kDefaultSampleSizePresent)) {
+ ALOGW("No sample size specified");
+ }
+#endif
}
if (flags & kSampleFlagsPresent) {
bytesPerSample += 4;
- } else if (mTrackFragmentHeaderInfo.mFlags
- & TrackFragmentHeaderInfo::kDefaultSampleFlagsPresent) {
- sampleFlags = mTrackFragmentHeaderInfo.mDefaultSampleFlags;
} else {
sampleFlags = mTrackFragmentHeaderInfo.mDefaultSampleFlags;
+#ifdef VERY_VERY_VERBOSE_LOGGING
+ // We don't expect this, but also want to avoid spamming the log if
+ // we hit this case.
+ if (!(mTrackFragmentHeaderInfo.mFlags
+ & TrackFragmentHeaderInfo::kDefaultSampleFlagsPresent)) {
+ ALOGW("No sample flags specified");
+ }
+#endif
}
if (flags & kSampleCompositionTimeOffsetPresent) {
@@ -5458,16 +5703,12 @@
// apply some sanity (vs strict legality) checks
//
- // clamp the count of entries in the trun box, to avoid spending forever parsing
- // this box. Clamping (vs error) lets us play *something*.
- // 1 million is about 400 msecs on a Pixel3, should be no more than a couple seconds
- // on the slowest devices.
- static constexpr uint32_t kMaxTrunSampleCount = 1000000;
+ static constexpr uint32_t kMaxTrunSampleCount = 10000;
if (sampleCount > kMaxTrunSampleCount) {
- ALOGW("b/123389881 clamp sampleCount(%u) @ kMaxTrunSampleCount(%u)",
+ ALOGW("b/123389881 sampleCount(%u) > kMaxTrunSampleCount(%u)",
sampleCount, kMaxTrunSampleCount);
android_errorWriteLog(0x534e4554, "124389881 count");
-
+ return -EINVAL;
}
}
@@ -5511,7 +5752,12 @@
tmp.duration = sampleDuration;
tmp.compositionOffset = sampleCtsOffset;
memset(tmp.iv, 0, sizeof(tmp.iv));
- mCurrentSamples.add(tmp);
+ if (mCurrentSamples.add(tmp) < 0) {
+ ALOGW("b/123389881 failed saving sample(n=%zu)", mCurrentSamples.size());
+ android_errorWriteLog(0x534e4554, "124389881 allocation");
+ mCurrentSamples.clear();
+ return NO_MEMORY;
+ }
dataOffset += sampleSize;
}
@@ -5598,6 +5844,7 @@
int64_t seekTimeUs;
ReadOptions::SeekMode mode;
+
if (options && options->getSeekTo(&seekTimeUs, &mode)) {
if (mIsHeif) {
@@ -5639,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;
@@ -5713,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) {
@@ -5721,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 {
@@ -5762,7 +6013,7 @@
}
}
- if (!mIsAVC && !mIsHEVC && !mIsAC4) {
+ if (!mIsAVC && !mIsHEVC && !(mIsDolbyVision && mNALLengthSize) && !mIsAC4) {
if (newBuffer) {
if (mIsPcm) {
// The twos' PCM block reader assumes that all samples has the same size.
@@ -6069,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()) {
@@ -6101,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);
@@ -6152,7 +6405,7 @@
AMediaFormat_setBuffer(bufmeta, AMEDIAFORMAT_KEY_CRYPTO_IV, iv, ivlength);
}
- if (!mIsAVC && !mIsHEVC) {
+ if (!mIsAVC && !mIsHEVC && !(mIsDolbyVision && mNALLengthSize)) {
if (newBuffer) {
if (!isInRange((size_t)0u, mBuffer->size(), size)) {
mBuffer->release();
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/mp4/SampleTable.cpp b/media/extractors/mp4/SampleTable.cpp
index 9dddf2c..59c8200 100644
--- a/media/extractors/mp4/SampleTable.cpp
+++ b/media/extractors/mp4/SampleTable.cpp
@@ -391,20 +391,11 @@
}
mTimeToSampleCount = U32_AT(&header[4]);
- if (mTimeToSampleCount > UINT32_MAX / (2 * sizeof(uint32_t))) {
- // Choose this bound because
- // 1) 2 * sizeof(uint32_t) is the amount of memory needed for one
- // time-to-sample entry in the time-to-sample table.
- // 2) mTimeToSampleCount is the number of entries of the time-to-sample
- // table.
- // 3) We hope that the table size does not exceed UINT32_MAX.
+ if (mTimeToSampleCount > (data_size - 8) / (2 * sizeof(uint32_t))) {
ALOGE("Time-to-sample table size too large.");
return ERROR_OUT_OF_RANGE;
}
- // Note: At this point, we know that mTimeToSampleCount * 2 will not
- // overflow because of the above condition.
-
uint64_t allocSize = (uint64_t)mTimeToSampleCount * 2 * sizeof(uint32_t);
mTotalSize += allocSize;
if (mTotalSize > kMaxTotalSize) {
@@ -540,6 +531,12 @@
}
uint64_t allocSize = (uint64_t)numSyncSamples * sizeof(uint32_t);
+ if (allocSize > data_size - 8) {
+ ALOGW("b/124771364 - allocSize(%lu) > size(%lu)",
+ (unsigned long)allocSize, (unsigned long)(data_size - 8));
+ android_errorWriteLog(0x534e4554, "124771364");
+ return ERROR_MALFORMED;
+ }
if (allocSize > kMaxTotalSize) {
ALOGE("Sync sample table size too large.");
return ERROR_OUT_OF_RANGE;
diff --git a/media/extractors/mpeg2/Android.bp b/media/extractors/mpeg2/Android.bp
index 14bd644..8d9145d 100644
--- a/media/extractors/mpeg2/Android.bp
+++ b/media/extractors/mpeg2/Android.bp
@@ -12,15 +12,10 @@
],
shared_libs: [
- "android.hardware.cas@1.0",
- "android.hardware.cas.native@1.0",
- "android.hidl.token@1.0-utils",
- "android.hidl.allocator@1.0",
- "libcrypto",
- "libhidlmemory",
- "libhidlbase",
- "liblog",
- "libmediandk",
+ "libcgrouprc#29",
+ "liblog#10000",
+ "libmediandk#29",
+ "libvndksupport#29",
],
header_libs: [
@@ -31,11 +26,24 @@
],
static_libs: [
+ "android.hardware.cas@1.0",
+ "android.hardware.cas.native@1.0",
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
+ "android.hidl.token@1.0",
+ "android.hidl.token@1.0-utils",
+ "libbase",
+ "libbinderthreadstate",
+ "libcutils",
+ "libhidlbase",
+ "libhidlmemory",
+ "libjsoncpp",
+ "libprocessgroup",
+ "libstagefright_esds",
"libstagefright_foundation_without_imemory",
+ "libstagefright_mpeg2extractor",
"libstagefright_mpeg2support",
"libutils",
- "libstagefright_mpeg2extractor",
- "libstagefright_esds",
],
name: "libmpeg2extractor",
@@ -51,11 +59,23 @@
version_script: "exports.lds",
sanitize: {
- cfi: true,
+ // 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/MPEG2TSExtractor.cpp b/media/extractors/mpeg2/MPEG2TSExtractor.cpp
index 50ce657..f4643c5 100644
--- a/media/extractors/mpeg2/MPEG2TSExtractor.cpp
+++ b/media/extractors/mpeg2/MPEG2TSExtractor.cpp
@@ -24,13 +24,13 @@
#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>
diff --git a/media/extractors/ogg/OggExtractor.cpp b/media/extractors/ogg/OggExtractor.cpp
index 72b94bb..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>
@@ -635,7 +635,8 @@
currentPageSamples -= mStartGranulePosition;
AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_VALID_SAMPLES, currentPageSamples);
}
- mCurGranulePosition = mCurrentPage.mGranulePosition - currentPageSamples;
+ (void) __builtin_sub_overflow(mCurrentPage.mGranulePosition, currentPageSamples,
+ &mCurGranulePosition);
}
int64_t timeUs = getTimeUsOfGranule(mCurGranulePosition);
@@ -1062,8 +1063,15 @@
size_t size = buffer->range_length();
if (size < kOpusHeaderSize
- || memcmp(data, "OpusHead", 8)
- || /* version = */ data[8] != 1) {
+ || memcmp(data, "OpusHead", 8)) {
+ return AMEDIA_ERROR_MALFORMED;
+ }
+ // allow both version 0 and 1. Per the opus specification:
+ // An earlier draft of the specification described a version 0, but the only difference
+ // between version 1 and version 0 is that version 0 did not specify the semantics for
+ // handling the version field
+ if ( /* version = */ data[8] > 1) {
+ ALOGW("no support for opus version %d", data[8]);
return AMEDIA_ERROR_MALFORMED;
}
@@ -1384,7 +1392,7 @@
return NULL;
}
- *confidence = 0.2f;
+ *confidence = 0.5f;
return CreateExtractor;
}
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/utils/AAudioArgsParser.h b/media/libaaudio/examples/utils/AAudioArgsParser.h
index 9115778..4bba436 100644
--- a/media/libaaudio/examples/utils/AAudioArgsParser.h
+++ b/media/libaaudio/examples/utils/AAudioArgsParser.h
@@ -38,12 +38,15 @@
aaudio_input_preset_t inputPreset) = nullptr;
static void (*s_setAllowedCapturePolicy)(AAudioStreamBuilder* builder,
aaudio_allowed_capture_policy_t usage) = nullptr;
+static void (*s_setPrivacySensitive)(AAudioStreamBuilder* builder,
+ bool privacySensitive) = nullptr;
static bool s_loadAttempted = false;
static aaudio_usage_t (*s_getUsage)(AAudioStream *stream) = nullptr;
static aaudio_content_type_t (*s_getContentType)(AAudioStream *stream) = nullptr;
static aaudio_input_preset_t (*s_getInputPreset)(AAudioStream *stream) = nullptr;
static aaudio_allowed_capture_policy_t (*s_getAllowedCapturePolicy)(AAudioStream *stream) = nullptr;
+static bool (*s_isPrivacySensitive)(AAudioStream *stream) = nullptr;
// Link to test functions in shared library.
static void loadFutureFunctions() {
@@ -68,6 +71,10 @@
dlsym(handle, "AAudioStreamBuilder_setAllowedCapturePolicy");
if (s_setAllowedCapturePolicy == nullptr) goto error;
+ s_setPrivacySensitive = (void (*)(AAudioStreamBuilder *, bool))
+ dlsym(handle, "AAudioStreamBuilder_setPrivacySensitive");
+ if (s_setPrivacySensitive == nullptr) goto error;
+
s_getUsage = (aaudio_usage_t (*)(AAudioStream *))
dlsym(handle, "AAudioStream_getUsage");
if (s_getUsage == nullptr) goto error;
@@ -83,6 +90,10 @@
s_getAllowedCapturePolicy = (aaudio_input_preset_t (*)(AAudioStream *))
dlsym(handle, "AAudioStream_getAllowedCapturePolicy");
if (s_getAllowedCapturePolicy == nullptr) goto error;
+
+ s_isPrivacySensitive = (bool (*)(AAudioStream *))
+ dlsym(handle, "AAudioStream_isPrivacySensitive");
+ if (s_isPrivacySensitive == nullptr) goto error;
}
return;
@@ -91,9 +102,11 @@
s_setUsage = nullptr;
s_setContentType = nullptr;
s_setInputPreset = nullptr;
+ s_setPrivacySensitive = nullptr;
s_getUsage = nullptr;
s_getContentType = nullptr;
s_getInputPreset = nullptr;
+ s_isPrivacySensitive = nullptr;
dlclose(handle);
return;
}
@@ -211,6 +224,14 @@
mFramesPerCallback = size;
}
+ int32_t isPrivacySensitive() const {
+ return mPrivacySensitive;
+ }
+
+ void setPrivacySensitive(int32_t privacySensitive) {
+ mPrivacySensitive = privacySensitive;
+ }
+
/**
* Apply these parameters to a stream builder.
* @param builder
@@ -234,12 +255,12 @@
}
if (s_setContentType != nullptr) {
s_setContentType(builder, mContentType);
- } else if (mUsage != AAUDIO_UNSPECIFIED){
+ } else if (mContentType != AAUDIO_UNSPECIFIED){
printf("WARNING: setContentType not supported");
}
if (s_setInputPreset != nullptr) {
s_setInputPreset(builder, mInputPreset);
- } else if (mUsage != AAUDIO_UNSPECIFIED){
+ } else if (mInputPreset != AAUDIO_UNSPECIFIED){
printf("WARNING: setInputPreset not supported");
}
@@ -249,6 +270,15 @@
} else if (mAllowedCapturePolicy != AAUDIO_UNSPECIFIED){
printf("WARNING: setAllowedCapturePolicy not supported");
}
+
+ if (mPrivacySensitive != PRIVACY_SENSITIVE_DEFAULT) {
+ if (s_setPrivacySensitive != nullptr) {
+ s_setPrivacySensitive(builder,
+ mPrivacySensitive == PRIVACY_SENSITIVE_ENABLED);
+ } else {
+ printf("WARNING: setPrivacySensitive not supported");
+ }
+ }
}
static constexpr int32_t kDefaultNumberOfBursts = 2;
@@ -270,6 +300,13 @@
int32_t mNumberOfBursts = kDefaultNumberOfBursts;
int32_t mFramesPerCallback = AAUDIO_UNSPECIFIED;
+
+ enum {
+ PRIVACY_SENSITIVE_DEFAULT = -1,
+ PRIVACY_SENSITIVE_DISABLED = 0,
+ PRIVACY_SENSITIVE_ENABLED = 1,
+ };
+ int32_t mPrivacySensitive = PRIVACY_SENSITIVE_DEFAULT;
};
class AAudioArgsParser : public AAudioParameters {
@@ -341,6 +378,9 @@
case 'z':
setFramesPerCallback(atoi(&arg[2]));
break;
+ case 'S':
+ setPrivacySensitive(atoi(&arg[2]));
+ break;
default:
unrecognized = true;
break;
@@ -399,6 +439,9 @@
printf(" -x to use EXCLUSIVE mode\n");
printf(" -y{contentType} eg. 1 for AAUDIO_CONTENT_TYPE_SPEECH\n");
printf(" -z{callbackSize} or block size, in frames, default = 0\n");
+ printf(" -S{0|1} set privacy Sensitive enabled or disabled\n");
+ printf(" 0 = disabled\n");
+ printf(" 1 = enabled\n");
}
static aaudio_performance_mode_t parseAllowedCapturePolicy(char c) {
@@ -506,10 +549,15 @@
getContentType(), s_getContentType(stream));
}
- if (AAudioStream_getDirection(stream) == AAUDIO_DIRECTION_INPUT
- && s_getInputPreset != nullptr) {
+ if (AAudioStream_getDirection(stream) == AAUDIO_DIRECTION_INPUT) {
+ if (s_getInputPreset != nullptr) {
printf(" InputPreset: requested = %d, actual = %d\n",
getInputPreset(), s_getInputPreset(stream));
+ }
+ if (s_isPrivacySensitive != nullptr) {
+ printf(" Privacy Sensitive: requested = %d, actual = %d\n",
+ isPrivacySensitive(), s_isPrivacySensitive(stream));
+ }
}
printf(" Is MMAP used? %s\n", AAudioStream_isMMapUsed(stream)
diff --git a/media/libaaudio/include/aaudio/AAudio.h b/media/libaaudio/include/aaudio/AAudio.h
index 8173e3c..5bebd61 100644
--- a/media/libaaudio/include/aaudio/AAudio.h
+++ b/media/libaaudio/include/aaudio/AAudio.h
@@ -724,7 +724,7 @@
* Available since API level 29.
*
* @param builder reference provided by AAudio_createStreamBuilder()
- * @param inputPreset the desired level of opt-out from being captured.
+ * @param capturePolicy the desired level of opt-out from being captured.
*/
AAUDIO_API void AAudioStreamBuilder_setAllowedCapturePolicy(AAudioStreamBuilder* builder,
aaudio_allowed_capture_policy_t capturePolicy) __INTRODUCED_IN(29);
@@ -759,6 +759,28 @@
AAUDIO_API void AAudioStreamBuilder_setSessionId(AAudioStreamBuilder* builder,
aaudio_session_id_t sessionId) __INTRODUCED_IN(28);
+
+/** Indicates whether this input stream must be marked as privacy sensitive or not.
+ *
+ * When true, this input stream is privacy sensitive and any concurrent capture
+ * is not permitted.
+ *
+ * This is off (false) by default except when the input preset is {@link #AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION}
+ * or {@link #AAUDIO_INPUT_PRESET_CAMCORDER}.
+ *
+ * Always takes precedence over default from input preset when set explicitly.
+ *
+ * Only relevant if the stream direction is {@link #AAUDIO_DIRECTION_INPUT}.
+ *
+ * Added in API level 30.
+ *
+ * @param builder reference provided by AAudio_createStreamBuilder()
+ * @param privacySensitive true if capture from this stream must be marked as privacy sensitive,
+ * false otherwise.
+ */
+AAUDIO_API void AAudioStreamBuilder_setPrivacySensitive(AAudioStreamBuilder* builder,
+ bool privacySensitive) __INTRODUCED_IN(30);
+
/**
* Return one of these values from the data callback function.
*/
@@ -1444,6 +1466,20 @@
AAUDIO_API aaudio_allowed_capture_policy_t AAudioStream_getAllowedCapturePolicy(
AAudioStream* stream) __INTRODUCED_IN(29);
+
+/**
+ * Return whether this input stream is marked as privacy sensitive or not.
+ *
+ * See {@link #AAudioStreamBuilder_setPrivacySensitive()}.
+ *
+ * Added in API level 30.
+ *
+ * @param stream reference provided by AAudioStreamBuilder_openStream()
+ * @return true if privacy sensitive, false otherwise
+ */
+AAUDIO_API bool AAudioStream_isPrivacySensitive(AAudioStream* stream)
+ __INTRODUCED_IN(30);
+
#ifdef __cplusplus
}
#endif
diff --git a/media/libaaudio/src/Android.bp b/media/libaaudio/src/Android.bp
index 25246b3..baada22 100644
--- a/media/libaaudio/src/Android.bp
+++ b/media/libaaudio/src/Android.bp
@@ -41,6 +41,19 @@
"libbinder",
],
+ sanitize: {
+ integer_overflow: true,
+ misc_undefined: ["bounds"],
+ diag: {
+ integer_overflow: true,
+ misc_undefined: ["bounds"],
+ no_recover: [
+ "bounds",
+ "integer",
+ ],
+ },
+ },
+
stubs: {
symbol_file: "libaaudio.map.txt",
versions: ["28"],
@@ -62,7 +75,7 @@
export_include_dirs: ["."],
header_libs: [
"libaaudio_headers",
- "libmedia_headers"
+ "libmedia_headers",
],
export_header_lib_headers: ["libaaudio_headers"],
@@ -121,4 +134,17 @@
"flowgraph/SourceI16.cpp",
"flowgraph/SourceI24.cpp",
],
+ 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/AAudioStreamConfiguration.cpp b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
index a987fab..b785f88 100644
--- a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
+++ b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
@@ -66,6 +66,8 @@
if (status != NO_ERROR) goto error;
status = parcel->writeInt32(getSessionId());
if (status != NO_ERROR) goto error;
+ status = parcel->writeInt32(isPrivacySensitive() ? 1 : 0);
+ if (status != NO_ERROR) goto error;
return NO_ERROR;
error:
ALOGE("%s(): write failed = %d", __func__, status);
@@ -111,7 +113,9 @@
status = parcel->readInt32(&value);
if (status != NO_ERROR) goto error;
setSessionId(value);
-
+ status = parcel->readInt32(&value);
+ if (status != NO_ERROR) goto error;
+ setPrivacySensitive(value == 1);
return NO_ERROR;
error:
ALOGE("%s(): read failed = %d", __func__, status);
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index fb276c2..bfad254 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -36,7 +36,6 @@
#include "binding/AAudioStreamConfiguration.h"
#include "binding/IAAudioService.h"
#include "binding/AAudioServiceMessage.h"
-#include "core/AudioGlobal.h"
#include "core/AudioStreamBuilder.h"
#include "fifo/FifoBuffer.h"
#include "utility/AudioClock.h"
@@ -117,6 +116,7 @@
request.getConfiguration().setUsage(getUsage());
request.getConfiguration().setContentType(getContentType());
request.getConfiguration().setInputPreset(getInputPreset());
+ request.getConfiguration().setPrivacySensitive(isPrivacySensitive());
request.getConfiguration().setBufferCapacity(builder.getBufferCapacity());
@@ -233,6 +233,26 @@
mCallbackBuffer = new uint8_t[callbackBufferSize];
}
+ // For debugging and analyzing the distribution of MMAP timestamps.
+ // For OUTPUT, use a NEGATIVE offset to move the CPU writes further BEFORE the HW reads.
+ // For INPUT, use a POSITIVE offset to move the CPU reads further AFTER the HW writes.
+ // You can use this offset to reduce glitching.
+ // You can also use this offset to force glitching. By iterating over multiple
+ // values you can reveal the distribution of the hardware timing jitter.
+ if (mAudioEndpoint.isFreeRunning()) { // MMAP?
+ int32_t offsetMicros = (getDirection() == AAUDIO_DIRECTION_OUTPUT)
+ ? AAudioProperty_getOutputMMapOffsetMicros()
+ : AAudioProperty_getInputMMapOffsetMicros();
+ // This log is used to debug some tricky glitch issues. Please leave.
+ ALOGD_IF(offsetMicros, "%s() - %s mmap offset = %d micros",
+ __func__,
+ (getDirection() == AAUDIO_DIRECTION_OUTPUT) ? "output" : "input",
+ offsetMicros);
+ mTimeOffsetNanos = offsetMicros * AAUDIO_NANOS_PER_MICROSECOND;
+ }
+
+ setBufferSize(capacity / 2); // Default buffer size to match Q
+
setState(AAUDIO_STREAM_STATE_OPEN);
return result;
@@ -479,7 +499,8 @@
#if LOG_TIMESTAMPS
logTimestamp(*message);
#endif
- processTimestamp(message->timestamp.position, message->timestamp.timestamp);
+ processTimestamp(message->timestamp.position,
+ message->timestamp.timestamp + mTimeOffsetNanos);
return AAUDIO_OK;
}
@@ -635,7 +656,7 @@
// Should we block?
if (timeoutNanoseconds == 0) {
break; // don't block
- } else if (framesLeft > 0) {
+ } else if (wakeTimeNanos != 0) {
if (!mAudioEndpoint.isFreeRunning()) {
// If there is software on the other end of the FIFO then it may get delayed.
// So wake up just a little after we expect it to be ready.
@@ -694,37 +715,39 @@
aaudio_result_t AudioStreamInternal::setBufferSize(int32_t requestedFrames) {
int32_t adjustedFrames = requestedFrames;
- int32_t actualFrames = 0;
- int32_t maximumSize = getBufferCapacity();
+ const int32_t maximumSize = getBufferCapacity() - mFramesPerBurst;
+ // The buffer size can be set to zero.
+ // This means that the callback may be called when the internal buffer becomes empty.
+ // This will be fine on some devices in ideal circumstances and will result in the
+ // lowest possible latency.
+ // If there are glitches then they should be detected as XRuns and the size can be increased.
+ static const int32_t minimumSize = 0;
// Clip to minimum size so that rounding up will work better.
- if (adjustedFrames < 1) {
- adjustedFrames = 1;
- }
+ adjustedFrames = std::max(minimumSize, adjustedFrames);
- if (adjustedFrames > maximumSize) {
- // Clip to maximum size.
+ // Prevent arithmetic overflow by clipping before we round.
+ if (adjustedFrames >= maximumSize) {
adjustedFrames = maximumSize;
} else {
// Round to the next highest burst size.
int32_t numBursts = (adjustedFrames + mFramesPerBurst - 1) / mFramesPerBurst;
adjustedFrames = numBursts * mFramesPerBurst;
- // Rounding may have gone above maximum.
- if (adjustedFrames > maximumSize) {
- adjustedFrames = maximumSize;
- }
}
- aaudio_result_t result = mAudioEndpoint.setBufferSizeInFrames(adjustedFrames, &actualFrames);
- if (result < 0) {
- return result;
- } else {
- return (aaudio_result_t) actualFrames;
- }
+ // Clip against the actual size from the endpoint.
+ int32_t actualFrames = 0;
+ mAudioEndpoint.setBufferSizeInFrames(maximumSize, &actualFrames);
+ // actualFrames should be <= maximumSize
+ adjustedFrames = std::min(actualFrames, adjustedFrames);
+
+ mBufferSizeInFrames = adjustedFrames;
+ ALOGV("%s(%d) returns %d", __func__, requestedFrames, adjustedFrames);
+ return (aaudio_result_t) adjustedFrames;
}
int32_t AudioStreamInternal::getBufferSize() const {
- return mAudioEndpoint.getBufferSizeInFrames();
+ return mBufferSizeInFrames;
}
int32_t AudioStreamInternal::getBufferCapacity() const {
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index 86c4698..596d37f 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -194,6 +194,7 @@
// By delaying slightly we can avoid waking up before other side is ready.
const int32_t mWakeupDelayNanos; // delay past typical wakeup jitter
const int32_t mMinimumSleepNanos; // minimum sleep while polling
+ int32_t mTimeOffsetNanos = 0; // add to time part of an MMAP timestamp
AudioEndpointParcelable mEndPointParcelable; // description of the buffers filled by service
EndpointDescriptor mEndpointDescriptor; // buffer description with resolved addresses
@@ -203,6 +204,9 @@
// Sometimes the hardware is operating with a different channel count from the app.
// Then we require conversion in AAudio.
int32_t mDeviceChannelCount = 0;
+
+ int32_t mBufferSizeInFrames = 0; // local threshold to control latency
+
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
index 366cc87..9684ee4 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
@@ -106,9 +106,10 @@
mNeedCatchUp.acknowledge();
}
- // If the write index passed the read index then consider it an overrun.
+ // If the capture buffer is full beyond capacity then consider it an overrun.
// For shared streams, the xRunCount is passed up from the service.
- if (mAudioEndpoint.isFreeRunning() && mAudioEndpoint.getEmptyFramesAvailable() < 0) {
+ if (mAudioEndpoint.isFreeRunning()
+ && mAudioEndpoint.getFullFramesAvailable() > mAudioEndpoint.getBufferCapacityInFrames()) {
mXRunCount++;
if (ATRACE_ENABLED()) {
ATRACE_INT("aaOverRuns", mXRunCount);
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index b8ef247..dc9f48c 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -167,8 +167,10 @@
ATRACE_INT("aaWrote", framesWritten);
}
+ // Sleep if there is too much data in the buffer.
// Calculate an ideal time to wake up.
- if (wakeTimePtr != nullptr && framesWritten >= 0) {
+ if (wakeTimePtr != nullptr
+ && (mAudioEndpoint.getFullFramesAvailable() >= getBufferSize())) {
// By default wake up a few milliseconds from now. // TODO review
int64_t wakeTime = currentNanoTime + (1 * AAUDIO_NANOS_PER_MILLISECOND);
aaudio_stream_state_t state = getState();
@@ -184,14 +186,10 @@
break;
case AAUDIO_STREAM_STATE_STARTED:
{
- // When do we expect the next read burst to occur?
-
- // Calculate frame position based off of the writeCounter because
- // the readCounter might have just advanced in the background,
- // causing us to sleep until a later burst.
- int64_t nextPosition = mAudioEndpoint.getDataWriteCounter() + mFramesPerBurst
- - mAudioEndpoint.getBufferSizeInFrames();
- wakeTime = mClockModel.convertPositionToTime(nextPosition);
+ // Sleep until the readCounter catches up and we only have
+ // the getBufferSize() frames of data sitting in the buffer.
+ int64_t nextReadPosition = mAudioEndpoint.getDataWriteCounter() - getBufferSize();
+ wakeTime = mClockModel.convertPositionToTime(nextReadPosition);
}
break;
default:
diff --git a/media/libaaudio/src/client/IsochronousClockModel.cpp b/media/libaaudio/src/client/IsochronousClockModel.cpp
index 9abdf53..bd46d05 100644
--- a/media/libaaudio/src/client/IsochronousClockModel.cpp
+++ b/media/libaaudio/src/client/IsochronousClockModel.cpp
@@ -18,22 +18,43 @@
//#define LOG_NDEBUG 0
#include <log/log.h>
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
#include <stdint.h>
#include <algorithm>
#include "utility/AudioClock.h"
+#include "utility/AAudioUtilities.h"
#include "IsochronousClockModel.h"
using namespace aaudio;
+using namespace android::audio_utils;
+
+#ifndef ICM_LOG_DRIFT
+#define ICM_LOG_DRIFT 0
+#endif // ICM_LOG_DRIFT
+
+// To enable the timestamp histogram, enter this before opening the stream:
+// adb root
+// adb shell setprop aaudio.log_mask 1
+// A histogram of the lateness of the timestamps will be cleared when the stream is started.
+// It will be updated when the model is stable and receives a timestamp,
+// and dumped to the log when the stream is stopped.
+
IsochronousClockModel::IsochronousClockModel()
: mMarkerFramePosition(0)
, mMarkerNanoTime(0)
, mSampleRate(48000)
- , mFramesPerBurst(64)
+ , mFramesPerBurst(48)
, mMaxMeasuredLatenessNanos(0)
+ , mLatenessForDriftNanos(kInitialLatenessForDriftNanos)
, mState(STATE_STOPPED)
{
+ if ((AAudioProperty_getLogMask() & AAUDIO_LOG_CLOCK_MODEL_HISTOGRAM) != 0) {
+ mHistogramMicros = std::make_unique<Histogram>(kHistogramBinCount,
+ kHistogramBinWidthMicros);
+ }
}
IsochronousClockModel::~IsochronousClockModel() {
@@ -49,6 +70,9 @@
ALOGV("start(nanos = %lld)\n", (long long) nanoTime);
mMarkerNanoTime = nanoTime;
mState = STATE_STARTING;
+ if (mHistogramMicros) {
+ mHistogramMicros->clear();
+ }
}
void IsochronousClockModel::stop(int64_t nanoTime) {
@@ -58,6 +82,9 @@
setPositionAndTime(convertTimeToPosition(nanoTime), nanoTime);
// TODO should we set position?
mState = STATE_STOPPED;
+ if (mHistogramMicros) {
+ dumpHistogram();
+ }
}
bool IsochronousClockModel::isStarting() const {
@@ -90,6 +117,7 @@
// ALOGD("processTimestamp() - mSampleRate = %d", mSampleRate);
// ALOGD("processTimestamp() - mState = %d", mState);
+ int64_t latenessNanos = nanosDelta - expectedNanosDelta;
switch (mState) {
case STATE_STOPPED:
break;
@@ -99,7 +127,7 @@
break;
case STATE_SYNCING:
// This will handle a burst of rapid transfer at the beginning.
- if (nanosDelta < expectedNanosDelta) {
+ if (latenessNanos < 0) {
setPositionAndTime(framePosition, nanoTime);
} else {
// ALOGD("processTimestamp() - advance to STATE_RUNNING");
@@ -107,65 +135,67 @@
}
break;
case STATE_RUNNING:
- if (nanosDelta < expectedNanosDelta) {
+ if (mHistogramMicros) {
+ mHistogramMicros->add(latenessNanos / AAUDIO_NANOS_PER_MICROSECOND);
+ }
+ // Modify estimated position based on lateness.
+ // This affects the "early" side of the window, which controls output glitches.
+ if (latenessNanos < 0) {
// Earlier than expected timestamp.
// This data is probably more accurate, so use it.
// Or we may be drifting due to a fast HW clock.
- //int microsDelta = (int) (nanosDelta / 1000);
- //int expectedMicrosDelta = (int) (expectedNanosDelta / 1000);
- //ALOGD("%s() - STATE_RUNNING - #%d, %4d micros EARLY",
- //__func__, mTimestampCount, expectedMicrosDelta - microsDelta);
-
setPositionAndTime(framePosition, nanoTime);
- } else if (nanosDelta > (expectedNanosDelta + (2 * mBurstPeriodNanos))) {
- // In this case we do not update mMaxMeasuredLatenessNanos because it
- // would force it too high.
- // mMaxMeasuredLatenessNanos should range from 1 to 2 * mBurstPeriodNanos
- //int32_t measuredLatenessNanos = (int32_t)(nanosDelta - expectedNanosDelta);
- //ALOGD("%s() - STATE_RUNNING - #%d, lateness %d - max %d = %4d micros VERY LATE",
- //__func__,
- //mTimestampCount,
- //measuredLatenessNanos / 1000,
- //mMaxMeasuredLatenessNanos / 1000,
- //(measuredLatenessNanos - mMaxMeasuredLatenessNanos) / 1000
- //);
-
- // This typically happens when we are modelling a service instead of a DSP.
- setPositionAndTime(framePosition, nanoTime - (2 * mBurstPeriodNanos));
- } else if (nanosDelta > (expectedNanosDelta + mMaxMeasuredLatenessNanos)) {
- //int32_t previousLatenessNanos = mMaxMeasuredLatenessNanos;
- mMaxMeasuredLatenessNanos = (int32_t)(nanosDelta - expectedNanosDelta);
-
- //ALOGD("%s() - STATE_RUNNING - #%d, newmax %d - oldmax %d = %4d micros LATE",
- //__func__,
- //mTimestampCount,
- //mMaxMeasuredLatenessNanos / 1000,
- //previousLatenessNanos / 1000,
- //(mMaxMeasuredLatenessNanos - previousLatenessNanos) / 1000
- //);
-
- // When we are late, it may be because of preemption in the kernel,
+#if ICM_LOG_DRIFT
+ int earlyDeltaMicros = (int) ((expectedNanosDelta - nanosDelta)/ 1000);
+ ALOGD("%s() - STATE_RUNNING - #%d, %4d micros EARLY",
+ __func__, mTimestampCount, earlyDeltaMicros);
+#endif
+ } else if (latenessNanos > mLatenessForDriftNanos) {
+ // When we are on the late side, it may be because of preemption in the kernel,
// or timing jitter caused by resampling in the DSP,
// or we may be drifting due to a slow HW clock.
// We add slight drift value just in case there is actual long term drift
// forward caused by a slower clock.
// If the clock is faster than the model will get pushed earlier
- // by the code in the preceding branch.
+ // by the code in the earlier branch.
// The two opposing forces should allow the model to track the real clock
// over a long time.
int64_t driftingTime = mMarkerNanoTime + expectedNanosDelta + kDriftNanos;
setPositionAndTime(framePosition, driftingTime);
- //ALOGD("%s() - #%d, max lateness = %d micros",
- //__func__,
- //mTimestampCount,
- //(int) (mMaxMeasuredLatenessNanos / 1000));
+#if ICM_LOG_DRIFT
+ ALOGD("%s() - STATE_RUNNING - #%d, DRIFT, lateness = %d micros",
+ __func__,
+ mTimestampCount,
+ (int) (latenessNanos / 1000));
+#endif
+ }
+
+ // Modify mMaxMeasuredLatenessNanos.
+ // This affects the "late" side of the window, which controls input glitches.
+ if (latenessNanos > mMaxMeasuredLatenessNanos) { // increase
+#if ICM_LOG_DRIFT
+ ALOGD("%s() - STATE_RUNNING - #%d, newmax %d - oldmax %d = %4d micros LATE",
+ __func__,
+ mTimestampCount,
+ (int) (latenessNanos / 1000),
+ mMaxMeasuredLatenessNanos / 1000,
+ (int) ((latenessNanos - mMaxMeasuredLatenessNanos) / 1000)
+ );
+#endif
+ mMaxMeasuredLatenessNanos = (int32_t) latenessNanos;
+ // Calculate upper region that will trigger a drift forwards.
+ mLatenessForDriftNanos = mMaxMeasuredLatenessNanos - (mMaxMeasuredLatenessNanos >> 4);
+ } else { // decrease
+ // If these is an outlier in lateness then mMaxMeasuredLatenessNanos can go high
+ // and stay there. So we slowly reduce mMaxMeasuredLatenessNanos for better
+ // long term stability. The two opposing forces will keep mMaxMeasuredLatenessNanos
+ // within a reasonable range.
+ mMaxMeasuredLatenessNanos -= kDriftNanos;
}
break;
default:
break;
}
-
-// ALOGD("processTimestamp() - mState = %d", mState);
}
void IsochronousClockModel::setSampleRate(int32_t sampleRate) {
@@ -181,9 +211,6 @@
// Update expected lateness based on sampleRate and framesPerBurst
void IsochronousClockModel::update() {
mBurstPeriodNanos = convertDeltaPositionToTime(mFramesPerBurst); // uses mSampleRate
- // Timestamps may be late by up to a burst because we are randomly sampling the time period
- // after the DSP position is actually updated.
- mMaxMeasuredLatenessNanos = mBurstPeriodNanos;
}
int64_t IsochronousClockModel::convertDeltaPositionToTime(int64_t framesDelta) const {
@@ -227,9 +254,7 @@
}
int32_t IsochronousClockModel::getLateTimeOffsetNanos() const {
- // This will never be < 0 because mMaxLatenessNanos starts at
- // mBurstPeriodNanos and only gets bigger.
- return (mMaxMeasuredLatenessNanos - mBurstPeriodNanos) + kExtraLatenessNanos;
+ return mMaxMeasuredLatenessNanos + kExtraLatenessNanos;
}
int64_t IsochronousClockModel::convertPositionToLatestTime(int64_t framePosition) const {
@@ -241,10 +266,19 @@
}
void IsochronousClockModel::dump() const {
- ALOGD("mMarkerFramePosition = %lld", (long long) mMarkerFramePosition);
- ALOGD("mMarkerNanoTime = %lld", (long long) mMarkerNanoTime);
+ ALOGD("mMarkerFramePosition = %" PRIu64, mMarkerFramePosition);
+ ALOGD("mMarkerNanoTime = %" PRIu64, mMarkerNanoTime);
ALOGD("mSampleRate = %6d", mSampleRate);
ALOGD("mFramesPerBurst = %6d", mFramesPerBurst);
ALOGD("mMaxMeasuredLatenessNanos = %6d", mMaxMeasuredLatenessNanos);
ALOGD("mState = %6d", mState);
}
+
+void IsochronousClockModel::dumpHistogram() const {
+ if (!mHistogramMicros) return;
+ std::istringstream istr(mHistogramMicros->dump());
+ std::string line;
+ while (std::getline(istr, line)) {
+ ALOGD("lateness, %s", line.c_str());
+ }
+}
diff --git a/media/libaaudio/src/client/IsochronousClockModel.h b/media/libaaudio/src/client/IsochronousClockModel.h
index 582bf4e..40f066b 100644
--- a/media/libaaudio/src/client/IsochronousClockModel.h
+++ b/media/libaaudio/src/client/IsochronousClockModel.h
@@ -18,6 +18,9 @@
#define ANDROID_AAUDIO_ISOCHRONOUS_CLOCK_MODEL_H
#include <stdint.h>
+
+#include <audio_utils/Histogram.h>
+
#include "utility/AudioClock.h"
namespace aaudio {
@@ -122,6 +125,8 @@
void dump() const;
+ void dumpHistogram() const;
+
private:
int32_t getLateTimeOffsetNanos() const;
@@ -134,21 +139,30 @@
};
// Amount of time to drift forward when we get a late timestamp.
- // This value was calculated to allow tracking of a clock with 50 ppm error.
- static constexpr int32_t kDriftNanos = 10 * 1000;
- // TODO review value of kExtraLatenessNanos
+ static constexpr int32_t kDriftNanos = 1 * 1000;
+ // Safety margin to add to the late edge of the timestamp window.
static constexpr int32_t kExtraLatenessNanos = 100 * 1000;
+ // Initial small threshold for causing a drift later in time.
+ static constexpr int32_t kInitialLatenessForDriftNanos = 10 * 1000;
- int64_t mMarkerFramePosition;
- int64_t mMarkerNanoTime;
+ static constexpr int32_t kHistogramBinWidthMicros = 50;
+ static constexpr int32_t kHistogramBinCount = 128;
+
+ int64_t mMarkerFramePosition; // Estimated HW position.
+ int64_t mMarkerNanoTime; // Estimated HW time.
int32_t mSampleRate;
- int32_t mFramesPerBurst;
- int32_t mBurstPeriodNanos;
+ int32_t mFramesPerBurst; // number of frames transferred at one time.
+ int32_t mBurstPeriodNanos; // Time between HW bursts.
// Includes mBurstPeriodNanos because we sample randomly over time.
int32_t mMaxMeasuredLatenessNanos;
- clock_model_state_t mState;
+ // Threshold for lateness that triggers a drift later in time.
+ int32_t mLatenessForDriftNanos;
+ clock_model_state_t mState; // State machine handles startup sequence.
- int32_t mTimestampCount = 0;
+ int32_t mTimestampCount = 0; // For logging.
+
+ // distribution of timestamps relative to earliest
+ std::unique_ptr<android::audio_utils::Histogram> mHistogramMicros;
void update();
};
diff --git a/media/libaaudio/src/core/AAudioAudio.cpp b/media/libaaudio/src/core/AAudioAudio.cpp
index 8040e6a..184e9cb 100644
--- a/media/libaaudio/src/core/AAudioAudio.cpp
+++ b/media/libaaudio/src/core/AAudioAudio.cpp
@@ -149,6 +149,12 @@
streamBuilder->setInputPreset(inputPreset);
}
+AAUDIO_API void AAudioStreamBuilder_setPrivacySensitive(AAudioStreamBuilder* builder,
+ bool privacySensitive) {
+ AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
+ streamBuilder->setPrivacySensitiveRequest(privacySensitive);
+}
+
AAUDIO_API void AAudioStreamBuilder_setBufferCapacityInFrames(AAudioStreamBuilder* builder,
int32_t frames)
{
@@ -507,3 +513,9 @@
AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
return audioStream->isMMap();
}
+
+AAUDIO_API bool AAudioStream_isPrivacySensitive(AAudioStream* stream)
+{
+ AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
+ return audioStream->isPrivacySensitive();
+}
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.cpp b/media/libaaudio/src/core/AAudioStreamParameters.cpp
index c9711da..58058f5 100644
--- a/media/libaaudio/src/core/AAudioStreamParameters.cpp
+++ b/media/libaaudio/src/core/AAudioStreamParameters.cpp
@@ -47,6 +47,7 @@
mContentType = other.mContentType;
mInputPreset = other.mInputPreset;
mAllowedCapturePolicy = other.mAllowedCapturePolicy;
+ mIsPrivacySensitive = other.mIsPrivacySensitive;
}
static aaudio_result_t isFormatValid(audio_format_t format) {
@@ -195,4 +196,5 @@
ALOGD("mContentType = %6d", mContentType);
ALOGD("mInputPreset = %6d", mInputPreset);
ALOGD("mAllowedCapturePolicy = %6d", mAllowedCapturePolicy);
+ ALOGD("mIsPrivacySensitive = %s", mIsPrivacySensitive ? "true" : "false");
}
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.h b/media/libaaudio/src/core/AAudioStreamParameters.h
index 2e21a8d..3e65b37 100644
--- a/media/libaaudio/src/core/AAudioStreamParameters.h
+++ b/media/libaaudio/src/core/AAudioStreamParameters.h
@@ -128,6 +128,14 @@
mSessionId = sessionId;
}
+ bool isPrivacySensitive() const {
+ return mIsPrivacySensitive;
+ }
+
+ void setPrivacySensitive(bool privacySensitive) {
+ mIsPrivacySensitive = privacySensitive;
+ }
+
/**
* @return bytes per frame of getFormat()
*/
@@ -158,6 +166,7 @@
int32_t mBufferCapacity = AAUDIO_UNSPECIFIED;
aaudio_allowed_capture_policy_t mAllowedCapturePolicy = AAUDIO_UNSPECIFIED;
aaudio_session_id_t mSessionId = AAUDIO_SESSION_ID_NONE;
+ bool mIsPrivacySensitive = false;
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index 6a8db22..1560e0c 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -91,6 +91,7 @@
if (mAllowedCapturePolicy == AAUDIO_UNSPECIFIED) {
mAllowedCapturePolicy = AAUDIO_ALLOW_CAPTURE_BY_ALL;
}
+ mIsPrivacySensitive = builder.isPrivacySensitive();
// callbacks
mFramesPerDataCallback = builder.getFramesPerDataCallback();
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index 044c979..b4ffcf2 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -234,6 +234,10 @@
return mSessionId;
}
+ bool isPrivacySensitive() const {
+ return mIsPrivacySensitive;
+ }
+
/**
* This is only valid after setSamplesPerFrame() and setFormat() have been called.
*/
@@ -543,6 +547,13 @@
mAllowedCapturePolicy = policy;
}
+ /**
+ * This should not be called after the open() call.
+ */
+ void setPrivacySensitive(bool privacySensitive) {
+ mIsPrivacySensitive = privacySensitive;
+ }
+
private:
aaudio_result_t safeStop();
@@ -565,6 +576,7 @@
aaudio_content_type_t mContentType = AAUDIO_UNSPECIFIED;
aaudio_input_preset_t mInputPreset = AAUDIO_UNSPECIFIED;
aaudio_allowed_capture_policy_t mAllowedCapturePolicy = AAUDIO_ALLOW_CAPTURE_BY_ALL;
+ bool mIsPrivacySensitive = false;
int32_t mSessionId = AAUDIO_UNSPECIFIED;
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.cpp b/media/libaaudio/src/core/AudioStreamBuilder.cpp
index 44f45b3..af28a59 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.cpp
+++ b/media/libaaudio/src/core/AudioStreamBuilder.cpp
@@ -158,6 +158,19 @@
return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
}
+ setPrivacySensitive(false);
+ if (mPrivacySensitiveReq == PRIVACY_SENSITIVE_DEFAULT) {
+ // When not explicitly requested, set privacy sensitive mode according to input preset:
+ // communication and camcorder captures are considered privacy sensitive by default.
+ aaudio_input_preset_t preset = getInputPreset();
+ if (preset == AAUDIO_INPUT_PRESET_CAMCORDER
+ || preset == AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION) {
+ setPrivacySensitive(true);
+ }
+ } else if (mPrivacySensitiveReq == PRIVACY_SENSITIVE_ENABLED) {
+ setPrivacySensitive(true);
+ }
+
result = builder_createStream(getDirection(), sharingMode, allowMMap, &audioStream);
if (result == AAUDIO_OK) {
// Open the stream using the parameters from the builder.
@@ -257,4 +270,5 @@
mFramesPerDataCallback);
ALOGI("usage = %6d, contentType = %d, inputPreset = %d, allowedCapturePolicy = %d",
getUsage(), getContentType(), getInputPreset(), getAllowedCapturePolicy());
+ ALOGI("privacy sensitive = %s", isPrivacySensitive() ? "true" : "false");
}
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.h b/media/libaaudio/src/core/AudioStreamBuilder.h
index 8149af2..d5fb80d 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.h
+++ b/media/libaaudio/src/core/AudioStreamBuilder.h
@@ -98,6 +98,12 @@
return this;
}
+ AudioStreamBuilder* setPrivacySensitiveRequest(bool privacySensitive) {
+ mPrivacySensitiveReq =
+ privacySensitive ? PRIVACY_SENSITIVE_ENABLED : PRIVACY_SENSITIVE_DISABLED;
+ return this;
+ }
+
aaudio_result_t build(AudioStream **streamPtr);
virtual aaudio_result_t validate() const override;
@@ -114,6 +120,14 @@
AAudioStream_errorCallback mErrorCallbackProc = nullptr;
void *mErrorCallbackUserData = nullptr;
+
+ enum {
+ PRIVACY_SENSITIVE_DEFAULT = -1,
+ PRIVACY_SENSITIVE_DISABLED = 0,
+ PRIVACY_SENSITIVE_ENABLED = 1,
+ };
+ typedef int32_t privacy_sensitive_t;
+ privacy_sensitive_t mPrivacySensitiveReq = PRIVACY_SENSITIVE_DEFAULT;
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/fifo/FifoControllerBase.cpp b/media/libaaudio/src/fifo/FifoControllerBase.cpp
index 66e247f..1dece0e 100644
--- a/media/libaaudio/src/fifo/FifoControllerBase.cpp
+++ b/media/libaaudio/src/fifo/FifoControllerBase.cpp
@@ -33,7 +33,9 @@
}
fifo_frames_t FifoControllerBase::getFullFramesAvailable() {
- return (fifo_frames_t) (getWriteCounter() - getReadCounter());
+ fifo_frames_t temp = 0;
+ __builtin_sub_overflow(getWriteCounter(), getReadCounter(), &temp);
+ return temp;
}
fifo_frames_t FifoControllerBase::getReadIndex() {
@@ -42,7 +44,9 @@
}
void FifoControllerBase::advanceReadIndex(fifo_frames_t numFrames) {
- setReadCounter(getReadCounter() + numFrames);
+ fifo_counter_t temp = 0;
+ __builtin_add_overflow(getReadCounter(), numFrames, &temp);
+ setReadCounter(temp);
}
fifo_frames_t FifoControllerBase::getEmptyFramesAvailable() {
@@ -55,7 +59,9 @@
}
void FifoControllerBase::advanceWriteIndex(fifo_frames_t numFrames) {
- setWriteCounter(getWriteCounter() + numFrames);
+ fifo_counter_t temp = 0;
+ __builtin_add_overflow(getWriteCounter(), numFrames, &temp);
+ setWriteCounter(temp);
}
void FifoControllerBase::setThreshold(fifo_frames_t threshold) {
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index 71efc30..54af580 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -142,11 +142,13 @@
const audio_source_t source =
AAudioConvert_inputPresetToAudioSource(builder.getInputPreset());
+ const audio_flags_mask_t attrFlags =
+ AAudioConvert_privacySensitiveToAudioFlagsMask(builder.isPrivacySensitive());
const audio_attributes_t attributes = {
.content_type = contentType,
.usage = AUDIO_USAGE_UNKNOWN, // only used for output
.source = source,
- .flags = AUDIO_FLAG_NONE, // Different than the AUDIO_INPUT_FLAGS
+ .flags = attrFlags, // Different than the AUDIO_INPUT_FLAGS
.tags = ""
};
diff --git a/media/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp
index cdd02c0..ef89697 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.cpp
+++ b/media/libaaudio/src/utility/AAudioUtilities.cpp
@@ -234,6 +234,11 @@
}
}
+audio_flags_mask_t AAudioConvert_privacySensitiveToAudioFlagsMask(
+ bool privacySensitive) {
+ return privacySensitive ? AUDIO_FLAG_CAPTURE_PRIVATE : AUDIO_FLAG_NONE;
+}
+
int32_t AAudioConvert_framesToBytes(int32_t numFrames,
int32_t bytesPerFrame,
int32_t *sizeInBytes) {
@@ -335,6 +340,34 @@
return prop;
}
+static int32_t AAudioProperty_getMMapOffsetMicros(const char *functionName,
+ const char *propertyName) {
+ const int32_t minMicros = -20000; // arbitrary
+ const int32_t defaultMicros = 0; // arbitrary
+ const int32_t maxMicros = 20000; // arbitrary
+ int32_t prop = property_get_int32(propertyName, defaultMicros);
+ if (prop < minMicros) {
+ ALOGW("%s: clipped %d to %d", functionName, prop, minMicros);
+ prop = minMicros;
+ } else if (prop > maxMicros) {
+ ALOGW("%s: clipped %d to %d", functionName, prop, minMicros);
+ prop = maxMicros;
+ }
+ return prop;
+}
+
+int32_t AAudioProperty_getInputMMapOffsetMicros() {
+ return AAudioProperty_getMMapOffsetMicros(__func__, AAUDIO_PROP_INPUT_MMAP_OFFSET_USEC);
+}
+
+int32_t AAudioProperty_getOutputMMapOffsetMicros() {
+ return AAudioProperty_getMMapOffsetMicros(__func__, AAUDIO_PROP_OUTPUT_MMAP_OFFSET_USEC);
+}
+
+int32_t AAudioProperty_getLogMask() {
+ return property_get_int32(AAUDIO_PROP_LOG_MASK, 0);
+}
+
aaudio_result_t AAudio_isFlushAllowed(aaudio_stream_state_t state) {
aaudio_result_t result = AAUDIO_OK;
switch (state) {
diff --git a/media/libaaudio/src/utility/AAudioUtilities.h b/media/libaaudio/src/utility/AAudioUtilities.h
index 76d0457..d2e4805 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.h
+++ b/media/libaaudio/src/utility/AAudioUtilities.h
@@ -92,33 +92,31 @@
audio_flags_mask_t AAudioConvert_allowCapturePolicyToAudioFlagsMask(
aaudio_allowed_capture_policy_t policy);
-// Note that this code may be replaced by Settings or by some other system configuration tool.
+audio_flags_mask_t AAudioConvert_privacySensitiveToAudioFlagsMask(
+ bool privacySensitive);
-#define AAUDIO_PROP_MMAP_POLICY "aaudio.mmap_policy"
+// Note that this code may be replaced by Settings or by some other system configuration tool.
/**
* Read system property.
* @return AAUDIO_UNSPECIFIED, AAUDIO_POLICY_NEVER or AAUDIO_POLICY_AUTO or AAUDIO_POLICY_ALWAYS
*/
int32_t AAudioProperty_getMMapPolicy();
-
-#define AAUDIO_PROP_MMAP_EXCLUSIVE_POLICY "aaudio.mmap_exclusive_policy"
+#define AAUDIO_PROP_MMAP_POLICY "aaudio.mmap_policy"
/**
* Read system property.
* @return AAUDIO_UNSPECIFIED, AAUDIO_POLICY_NEVER or AAUDIO_POLICY_AUTO or AAUDIO_POLICY_ALWAYS
*/
int32_t AAudioProperty_getMMapExclusivePolicy();
-
-#define AAUDIO_PROP_MIXER_BURSTS "aaudio.mixer_bursts"
+#define AAUDIO_PROP_MMAP_EXCLUSIVE_POLICY "aaudio.mmap_exclusive_policy"
/**
* Read system property.
* @return number of bursts per AAudio service mixer cycle
*/
int32_t AAudioProperty_getMixerBursts();
-
-#define AAUDIO_PROP_HW_BURST_MIN_USEC "aaudio.hw_burst_min_usec"
+#define AAUDIO_PROP_MIXER_BURSTS "aaudio.mixer_bursts"
/**
* Read a system property that specifies the number of extra microseconds that a thread
@@ -130,7 +128,6 @@
* @return number of microseconds to delay the wakeup.
*/
int32_t AAudioProperty_getWakeupDelayMicros();
-
#define AAUDIO_PROP_WAKEUP_DELAY_USEC "aaudio.wakeup_delay_usec"
/**
@@ -139,7 +136,6 @@
* @return minimum number of microseconds to sleep.
*/
int32_t AAudioProperty_getMinimumSleepMicros();
-
#define AAUDIO_PROP_MINIMUM_SLEEP_USEC "aaudio.minimum_sleep_usec"
/**
@@ -153,7 +149,35 @@
* @return minimum number of microseconds for a MMAP HW burst
*/
int32_t AAudioProperty_getHardwareBurstMinMicros();
+#define AAUDIO_PROP_HW_BURST_MIN_USEC "aaudio.hw_burst_min_usec"
+/**
+ * Read a system property that specifies an offset that will be added to MMAP timestamps.
+ * This can be used to correct bias in the timestamp.
+ * It can also be used to analyze the time distribution of the timestamp
+ * by progressively modifying the offset and listening for glitches.
+ *
+ * @return number of microseconds to offset the time part of an MMAP timestamp
+ */
+int32_t AAudioProperty_getInputMMapOffsetMicros();
+#define AAUDIO_PROP_INPUT_MMAP_OFFSET_USEC "aaudio.in_mmap_offset_usec"
+
+int32_t AAudioProperty_getOutputMMapOffsetMicros();
+#define AAUDIO_PROP_OUTPUT_MMAP_OFFSET_USEC "aaudio.out_mmap_offset_usec"
+
+// These are powers of two that can be combined as a bit mask.
+// AAUDIO_LOG_CLOCK_MODEL_HISTOGRAM must be enabled before the stream is opened.
+#define AAUDIO_LOG_CLOCK_MODEL_HISTOGRAM 1
+#define AAUDIO_LOG_RESERVED_2 2
+#define AAUDIO_LOG_RESERVED_4 4
+#define AAUDIO_LOG_RESERVED_8 8
+
+/**
+ * Use a mask to enable various logs in AAudio.
+ * @return mask that enables various AAudio logs, such as AAUDIO_LOG_CLOCK_MODEL_HISTOGRAM
+ */
+int32_t AAudioProperty_getLogMask();
+#define AAUDIO_PROP_LOG_MASK "aaudio.log_mask"
/**
* Is flush allowed for the given state?
diff --git a/media/libaaudio/tests/Android.bp b/media/libaaudio/tests/Android.bp
index 19cd0a0..73fd896 100644
--- a/media/libaaudio/tests/Android.bp
+++ b/media/libaaudio/tests/Android.bp
@@ -215,3 +215,14 @@
srcs: ["test_full_queue.cpp"],
shared_libs: ["libaaudio"],
}
+
+cc_test {
+ name: "test_histogram",
+ defaults: ["libaaudio_tests_defaults"],
+ srcs: ["test_histogram.cpp"],
+ shared_libs: [
+ "libaudioutils",
+ "libcutils",
+ "libutils",
+ ],
+}
diff --git a/media/libaaudio/tests/test_histogram.cpp b/media/libaaudio/tests/test_histogram.cpp
new file mode 100644
index 0000000..431373d
--- /dev/null
+++ b/media/libaaudio/tests/test_histogram.cpp
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+/*
+ * Test Histogram
+ */
+
+#include <iostream>
+
+#include <gtest/gtest.h>
+
+#include <audio_utils/Histogram.h>
+
+using namespace android::audio_utils;
+
+static constexpr int32_t kBinWidth = 10;
+static constexpr int32_t kNumBins = 20;
+
+TEST(test_histogram, module_sinki16) {
+ Histogram histogram(kNumBins, kBinWidth);
+ ASSERT_EQ(kNumBins, histogram.getNumBinsInRange());
+
+ // Is it clear initially?
+ for (int i = 0; i < kNumBins; i++) {
+ ASSERT_EQ(0, histogram.getCount(i));
+ }
+ ASSERT_EQ(0, histogram.getCountBelowRange());
+ ASSERT_EQ(0, histogram.getCountAboveRange());
+ ASSERT_EQ(0, histogram.getCount());
+
+ // Add some items.
+ histogram.add(27);
+ histogram.add(53);
+ histogram.add(171);
+ histogram.add(23);
+
+ // Did they count correctly.
+ ASSERT_EQ(2, histogram.getCount(2)); // For items 27 and 23
+ ASSERT_EQ(3, histogram.getLastItemNumber(2)); // Item 23 was the 0,1,2,3th item added.
+ ASSERT_EQ(1, histogram.getCount(5)); // For item 53
+ ASSERT_EQ(1, histogram.getLastItemNumber(5)); // item 53 was the second item added.
+ ASSERT_EQ(1, histogram.getCount(17)); // For item 171
+ ASSERT_EQ(4, histogram.getCount()); // A total of four items were added.
+
+ // Add values out of range.
+ histogram.add(-5);
+ ASSERT_EQ(1, histogram.getCountBelowRange()); // -5 is below zero.
+ ASSERT_EQ(0, histogram.getCountAboveRange());
+ ASSERT_EQ(5, histogram.getCount());
+
+ histogram.add(200);
+ ASSERT_EQ(1, histogram.getCountBelowRange());
+ ASSERT_EQ(1, histogram.getCountAboveRange()); // 200 is above top bin
+ ASSERT_EQ(6, histogram.getCount());
+
+ // Try to read values out of range. Should not crash.
+ // Legal index range is 0 to numBins-1
+ histogram.add(-1);
+ histogram.add(kNumBins);
+ ASSERT_EQ(0, histogram.getCount(-1)); // edge
+ ASSERT_EQ(0, histogram.getCount(kNumBins)); // edge
+ ASSERT_EQ(0, histogram.getCount(-1234)); // extreme
+ ASSERT_EQ(0, histogram.getCount(98765)); // extreme
+ ASSERT_EQ(0, histogram.getLastItemNumber(-1));
+ ASSERT_EQ(0, histogram.getLastItemNumber(kNumBins));
+
+ // Clear all the counts.
+ histogram.clear();
+ // Is it clear?
+ for (int i = 0; i < kNumBins; i++) {
+ ASSERT_EQ(0, histogram.getCount(i));
+ }
+ ASSERT_EQ(0, histogram.getCountBelowRange());
+ ASSERT_EQ(0, histogram.getCountAboveRange());
+ ASSERT_EQ(0, histogram.getCount());
+}
diff --git a/media/libaaudio/tests/test_various.cpp b/media/libaaudio/tests/test_various.cpp
index 4b065c9..935d88b 100644
--- a/media/libaaudio/tests/test_various.cpp
+++ b/media/libaaudio/tests/test_various.cpp
@@ -441,7 +441,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 +469,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 28190ea..7d5b690 100644
--- a/media/libaudioclient/AudioEffect.cpp
+++ b/media/libaudioclient/AudioEffect.cpp
@@ -166,7 +166,11 @@
mIEffect = iEffect;
mCblkMemory = cblk;
- mCblk = static_cast<effect_param_cblk_t*>(cblk->pointer());
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ mCblk = static_cast<effect_param_cblk_t*>(cblk->unsecurePointer());
int bufOffset = ((sizeof(effect_param_cblk_t) - 1) / sizeof(int) + 1) * sizeof(int);
mCblk->buffer = (uint8_t *)mCblk + bufOffset;
diff --git a/media/libaudioclient/AudioProductStrategy.cpp b/media/libaudioclient/AudioProductStrategy.cpp
index 0e1dfac..cff72fd 100644
--- a/media/libaudioclient/AudioProductStrategy.cpp
+++ b/media/libaudioclient/AudioProductStrategy.cpp
@@ -70,6 +70,7 @@
return NO_ERROR;
}
+// Keep in sync with android/media/audiopolicy/AudioProductStrategy#attributeMatches
bool AudioProductStrategy::attributesMatches(const audio_attributes_t refAttributes,
const audio_attributes_t clientAttritubes)
{
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index a1b04ca..a438c77 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,37 @@
#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());
}
}
// 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;
}
@@ -262,8 +262,12 @@
}
if (pAttributes == NULL) {
- memset(&mAttributes, 0, sizeof(audio_attributes_t));
+ mAttributes = AUDIO_ATTRIBUTES_INITIALIZER;
mAttributes.source = inputSource;
+ if (inputSource == AUDIO_SOURCE_VOICE_COMMUNICATION
+ || inputSource == AUDIO_SOURCE_CAMCORDER) {
+ mAttributes.flags |= AUDIO_FLAG_CAPTURE_PRIVATE;
+ }
} else {
// stream type shouldn't be looked at, this track has audio attributes
memcpy(&mAttributes, pAttributes, sizeof(audio_attributes_t));
@@ -759,7 +763,11 @@
status = NO_INIT;
goto exit;
}
- iMemPointer = output.cblk ->pointer();
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ iMemPointer = output.cblk ->unsecurePointer();
if (iMemPointer == NULL) {
ALOGE("%s(%d): Could not get control block pointer", __func__, mPortId);
status = NO_INIT;
@@ -774,7 +782,11 @@
if (output.buffers == 0) {
buffers = cblk + 1;
} else {
- buffers = output.buffers->pointer();
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ buffers = output.buffers->unsecurePointer();
if (buffers == NULL) {
ALOGE("%s(%d): Could not get buffer pointer", __func__, mPortId);
status = NO_INIT;
@@ -884,7 +896,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;
@@ -902,7 +913,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
@@ -939,6 +950,7 @@
audioBuffer->frameCount = buffer.mFrameCount;
audioBuffer->size = buffer.mFrameCount * mFrameSize;
audioBuffer->raw = buffer.mRaw;
+ audioBuffer->sequence = oldSequence;
if (nonContig != NULL) {
*nonContig = buffer.mNonContig;
}
@@ -959,6 +971,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);
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index 480930b..0742091 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -1017,6 +1017,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();
@@ -1532,6 +1542,13 @@
return aps->setRttEnabled(enabled);
}
+bool AudioSystem::isCallScreenModeSupported()
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return false;
+ return aps->isCallScreenModeSupported();
+}
+
status_t AudioSystem::setPreferredDeviceForStrategy(product_strategy_t strategy,
const AudioDeviceTypeAddr &device)
{
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 4a80cd3..3151feb 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;
}
@@ -406,7 +406,7 @@
mDoNotReconnect = doNotReconnect;
ALOGV_IF(sharedBuffer != 0, "%s(): sharedBuffer: %p, size: %zu",
- __func__, sharedBuffer->pointer(), sharedBuffer->size());
+ __func__, sharedBuffer->unsecurePointer(), sharedBuffer->size());
ALOGV("%s(): streamType %d frameCount %zu flags %04x",
__func__, streamType, frameCount, flags);
@@ -1508,7 +1508,11 @@
status = NO_INIT;
goto exit;
}
- void *iMemPointer = iMem->pointer();
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ void *iMemPointer = iMem->unsecurePointer();
if (iMemPointer == NULL) {
ALOGE("%s(%d): Could not get control block pointer", __func__, mPortId);
status = NO_INIT;
@@ -1563,7 +1567,11 @@
if (mSharedBuffer == 0) {
buffers = cblk + 1;
} else {
- buffers = mSharedBuffer->pointer();
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ buffers = mSharedBuffer->unsecurePointer();
if (buffers == NULL) {
ALOGE("%s(%d): Could not get buffer pointer", __func__, mPortId);
status = NO_INIT;
diff --git a/media/libaudioclient/AudioTrackShared.cpp b/media/libaudioclient/AudioTrackShared.cpp
index ee6c335..f1f8f9c 100644
--- a/media/libaudioclient/AudioTrackShared.cpp
+++ b/media/libaudioclient/AudioTrackShared.cpp
@@ -984,8 +984,9 @@
// ---------------------------------------------------------------------------
StaticAudioTrackServerProxy::StaticAudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers,
- size_t frameCount, size_t frameSize)
- : AudioTrackServerProxy(cblk, buffers, frameCount, frameSize),
+ size_t frameCount, size_t frameSize, uint32_t sampleRate)
+ : AudioTrackServerProxy(cblk, buffers, frameCount, frameSize, false /*clientInServer*/,
+ sampleRate),
mObserver(&cblk->u.mStatic.mSingleStateQueue),
mPosLoopMutator(&cblk->u.mStatic.mPosLoopQueue),
mFramesReadySafe(frameCount), mFramesReady(frameCount),
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index 53d46f1..cbc98bd 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -342,11 +342,11 @@
return reply.readInt32();
}
- virtual void setRecordSilenced(uid_t uid, bool silenced)
+ virtual void setRecordSilenced(audio_port_handle_t portId, bool silenced)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32(uid);
+ data.writeInt32(portId);
data.writeInt32(silenced ? 1 : 0);
remote()->transact(SET_RECORD_SILENCED, data, &reply);
}
@@ -1176,11 +1176,9 @@
} break;
case SET_RECORD_SILENCED: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
- uid_t uid = data.readInt32();
- audio_source_t source;
- data.read(&source, sizeof(audio_source_t));
+ audio_port_handle_t portId = data.readInt32();
bool silenced = data.readInt32() == 1;
- setRecordSilenced(uid, silenced);
+ setRecordSilenced(portId, silenced);
return NO_ERROR;
} break;
case SET_PARAMETERS: {
diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp
index 0facaf8..87802a1 100644
--- a/media/libaudioclient/IAudioPolicyService.cpp
+++ b/media/libaudioclient/IAudioPolicyService.cpp
@@ -105,9 +105,11 @@
SET_ALLOWED_CAPTURE_POLICY,
MOVE_EFFECTS_TO_IO,
SET_RTT_ENABLED,
+ IS_CALL_SCREEN_MODE_SUPPORTED,
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
@@ -1288,6 +1290,17 @@
return static_cast<status_t>(reply.readInt32());
}
+ virtual bool isCallScreenModeSupported()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ status_t status = remote()->transact(IS_CALL_SCREEN_MODE_SUPPORTED, data, &reply);
+ if (status != NO_ERROR) {
+ return false;
+ }
+ return reply.readBool();
+ }
+
virtual status_t setPreferredDeviceForStrategy(product_strategy_t strategy,
const AudioDeviceTypeAddr &device)
{
@@ -1336,6 +1349,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");
@@ -1359,8 +1407,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());
@@ -1398,10 +1444,14 @@
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 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: {
if (!isServiceUid(IPCThreadState::self()->getCallingUid())) {
ALOGW("%s: transaction %d received from PID %d unauthorized UID %d",
__func__, code, IPCThreadState::self()->getCallingPid(),
@@ -2014,8 +2064,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;
@@ -2031,8 +2079,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);
@@ -2292,7 +2338,6 @@
reply->writeBool(isSupported);
return NO_ERROR;
}
-
case SET_UID_DEVICE_AFFINITY: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
const uid_t uid = (uid_t) data.readInt32();
@@ -2424,6 +2469,13 @@
return NO_ERROR;
}
+ case IS_CALL_SCREEN_MODE_SUPPORTED: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ bool isAvailable = isCallScreenModeSupported();
+ reply->writeBool(isAvailable);
+ return NO_ERROR;
+ }
+
case SET_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
product_strategy_t strategy = (product_strategy_t) data.readUint32();
@@ -2458,6 +2510,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/IAudioTrack.cpp b/media/libaudioclient/IAudioTrack.cpp
index 83a568a..6219e7a 100644
--- a/media/libaudioclient/IAudioTrack.cpp
+++ b/media/libaudioclient/IAudioTrack.cpp
@@ -62,7 +62,7 @@
status_t status = remote()->transact(GET_CBLK, data, &reply);
if (status == NO_ERROR) {
cblk = interface_cast<IMemory>(reply.readStrongBinder());
- if (cblk != 0 && cblk->pointer() == NULL) {
+ if (cblk != 0 && cblk->unsecurePointer() == NULL) {
cblk.clear();
}
}
diff --git a/media/libaudioclient/IEffect.cpp b/media/libaudioclient/IEffect.cpp
index ce72dae..5d47dff 100644
--- a/media/libaudioclient/IEffect.cpp
+++ b/media/libaudioclient/IEffect.cpp
@@ -122,7 +122,7 @@
status_t status = remote()->transact(GET_CBLK, data, &reply);
if (status == NO_ERROR) {
cblk = interface_cast<IMemory>(reply.readStrongBinder());
- if (cblk != 0 && cblk->pointer() == NULL) {
+ if (cblk != 0 && cblk->unsecurePointer() == NULL) {
cblk.clear();
}
}
diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h
index a3c0fe4..b510378 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;
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index a86297d..ae8b59c 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -279,6 +279,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,
@@ -397,6 +399,8 @@
static status_t setRttEnabled(bool enabled);
+ static bool isCallScreenModeSupported();
+
/**
* Send audio HAL server process pids to native audioserver process for use
* when generating audio HAL servers tombstones
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index df5eabc..cfbce03 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>
@@ -400,7 +400,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.
@@ -1230,19 +1230,19 @@
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;
};
diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h
index 1c35ff0..8703f55 100644
--- a/media/libaudioclient/include/media/IAudioFlinger.h
+++ b/media/libaudioclient/include/media/IAudioFlinger.h
@@ -71,8 +71,12 @@
return DEAD_OBJECT;
}
if (parcel->readInt32() != 0) {
+ // TODO: Using unsecurePointer() has some associated security
+ // pitfalls (see declaration for details).
+ // Either document why it is safe in this case or address
+ // the issue (e.g. by copying).
sharedBuffer = interface_cast<IMemory>(parcel->readStrongBinder());
- if (sharedBuffer == 0 || sharedBuffer->pointer() == NULL) {
+ if (sharedBuffer == 0 || sharedBuffer->unsecurePointer() == NULL) {
return BAD_VALUE;
}
}
@@ -270,13 +274,21 @@
(void)parcel->read(&inputId, sizeof(audio_io_handle_t));
if (parcel->readInt32() != 0) {
cblk = interface_cast<IMemory>(parcel->readStrongBinder());
- if (cblk == 0 || cblk->pointer() == NULL) {
+ // TODO: Using unsecurePointer() has some associated security
+ // pitfalls (see declaration for details).
+ // Either document why it is safe in this case or address
+ // the issue (e.g. by copying).
+ if (cblk == 0 || cblk->unsecurePointer() == NULL) {
return BAD_VALUE;
}
}
if (parcel->readInt32() != 0) {
buffers = interface_cast<IMemory>(parcel->readStrongBinder());
- if (buffers == 0 || buffers->pointer() == NULL) {
+ // TODO: Using unsecurePointer() has some associated security
+ // pitfalls (see declaration for details).
+ // Either document why it is safe in this case or address
+ // the issue (e.g. by copying).
+ if (buffers == 0 || buffers->unsecurePointer() == NULL) {
return BAD_VALUE;
}
}
@@ -385,7 +397,7 @@
// mic mute/state
virtual status_t setMicMute(bool state) = 0;
virtual bool getMicMute() const = 0;
- virtual void setRecordSilenced(uid_t uid, bool silenced) = 0;
+ virtual void setRecordSilenced(audio_port_handle_t portId, bool silenced) = 0;
virtual status_t setParameters(audio_io_handle_t ioHandle,
const String8& keyValuePairs) = 0;
@@ -397,7 +409,7 @@
// Thus the IAudioFlingerClient must be a singleton per process.
virtual void registerClient(const sp<IAudioFlingerClient>& client) = 0;
- // retrieve the audio recording buffer size
+ // retrieve the audio recording buffer size in bytes
// FIXME This API assumes a route, and so should be deprecated.
virtual size_t getInputBufferSize(uint32_t sampleRate, audio_format_t format,
audio_channel_mask_t channelMask) const = 0;
diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h
index 9b91d6d..742762d 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,
@@ -224,6 +226,8 @@
virtual status_t setRttEnabled(bool enabled) = 0;
+ virtual bool isCallScreenModeSupported() = 0;
+
virtual status_t setPreferredDeviceForStrategy(product_strategy_t strategy,
const AudioDeviceTypeAddr &device) = 0;
diff --git a/media/libaudiofoundation/Android.bp b/media/libaudiofoundation/Android.bp
index 8dcc421..93bc4d9 100644
--- a/media/libaudiofoundation/Android.bp
+++ b/media/libaudiofoundation/Android.bp
@@ -12,7 +12,7 @@
],
}
-cc_library_shared {
+cc_library {
name: "libaudiofoundation",
vendor_available: true,
double_loadable: true,
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 42c16c0..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(
@@ -289,6 +292,10 @@
sinkMetadata.tracks[0].destination.device(std::move(hidlOutputDevice));
}
#endif
+#if MAJOR_VERSION <= 5
+ // Some flags were specific to framework and must not leak to the HAL.
+ flags = static_cast<audio_input_flags_t>(flags & ~AUDIO_INPUT_FLAG_DIRECT);
+#endif
Return<void> ret = mDevice->openInputStream(
handle,
hidlDevice,
@@ -317,19 +324,47 @@
const struct audio_port_config *sinks,
audio_patch_handle_t *patch) {
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) {
@@ -385,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/libaudioclient/include/media/AudioMixer.h b/media/libaudioprocessing/include/media/AudioMixer.h
similarity index 100%
rename from media/libaudioclient/include/media/AudioMixer.h
rename to media/libaudioprocessing/include/media/AudioMixer.h
diff --git a/media/libmedia/include/media/BufferProviders.h b/media/libaudioprocessing/include/media/BufferProviders.h
similarity index 100%
rename from media/libmedia/include/media/BufferProviders.h
rename to media/libaudioprocessing/include/media/BufferProviders.h
diff --git a/media/libdatasource/include/datasource/HTTPBase.h b/media/libdatasource/include/datasource/HTTPBase.h
index 8b20187..656e85e 100644
--- a/media/libdatasource/include/datasource/HTTPBase.h
+++ b/media/libdatasource/include/datasource/HTTPBase.h
@@ -21,6 +21,7 @@
#include <media/DataSource.h>
#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/MediaErrors.h>
+#include <utils/KeyedVector.h>
#include <utils/List.h>
#include <utils/threads.h>
diff --git a/media/libeffects/downmix/Android.bp b/media/libeffects/downmix/Android.bp
index 9c82b1d..2a2f36e 100644
--- a/media/libeffects/downmix/Android.bp
+++ b/media/libeffects/downmix/Android.bp
@@ -6,6 +6,7 @@
srcs: ["EffectDownmix.c"],
shared_libs: [
+ "libaudioutils",
"libcutils",
"liblog",
],
@@ -23,5 +24,4 @@
"libaudioeffects",
"libhardware_headers",
],
- static_libs: ["libaudioutils" ],
}
diff --git a/media/libeffects/lvm/lib/Common/lib/VectorArithmetic.h b/media/libeffects/lvm/lib/Common/lib/VectorArithmetic.h
index 7468a90..10eedd9 100644
--- a/media/libeffects/lvm/lib/Common/lib/VectorArithmetic.h
+++ b/media/libeffects/lvm/lib/Common/lib/VectorArithmetic.h
@@ -53,6 +53,7 @@
LVM_INT16 NrFrames,
LVM_INT32 NrChannels);
void Copy_Float_Stereo_Mc( const LVM_FLOAT *src,
+ LVM_FLOAT *StereoOut,
LVM_FLOAT *dst,
LVM_INT16 NrFrames,
LVM_INT32 NrChannels);
diff --git a/media/libeffects/lvm/lib/Common/src/Copy_16.c b/media/libeffects/lvm/lib/Common/src/Copy_16.c
index 3858450..3eb3c14 100644
--- a/media/libeffects/lvm/lib/Common/src/Copy_16.c
+++ b/media/libeffects/lvm/lib/Common/src/Copy_16.c
@@ -117,30 +117,31 @@
}
}
-// Merge a multichannel source with stereo contained in dst, to dst.
+// Merge a multichannel source with stereo contained in StereoOut, to dst.
void Copy_Float_Stereo_Mc(const LVM_FLOAT *src,
+ LVM_FLOAT *StereoOut,
LVM_FLOAT *dst,
LVM_INT16 NrFrames, /* Number of frames*/
LVM_INT32 NrChannels)
{
LVM_INT16 ii, jj;
- LVM_FLOAT *src_st = dst + 2 * (NrFrames - 1);
- // repack dst which carries stereo information
+ // pack dst with stereo information of StereoOut
// together with the upper channels of src.
+ StereoOut += 2 * (NrFrames - 1);
dst += NrChannels * (NrFrames - 1);
src += NrChannels * (NrFrames - 1);
for (ii = NrFrames; ii != 0; ii--)
{
- dst[1] = src_st[1];
- dst[0] = src_st[0]; // copy 1 before 0 is required for NrChannels == 3.
+ dst[1] = StereoOut[1];
+ dst[0] = StereoOut[0]; // copy 1 before 0 is required for NrChannels == 3.
for (jj = 2; jj < NrChannels; jj++)
{
dst[jj] = src[jj];
}
dst -= NrChannels;
src -= NrChannels;
- src_st -= 2;
+ StereoOut -= 2;
}
}
#endif
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Private.h b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Private.h
index ab8ccd1..c8df8e4 100644
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Private.h
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Private.h
@@ -60,7 +60,11 @@
#define LVCS_COMPGAINFRAME 64 /* Compressor gain update interval */
/* Memory */
+#ifdef SUPPORT_MC
+#define LVCS_SCRATCHBUFFERS 8 /* Number of buffers required for inplace processing */
+#else
#define LVCS_SCRATCHBUFFERS 6 /* Number of buffers required for inplace processing */
+#endif
#ifdef SUPPORT_MC
/*
* The Concert Surround module applies processing only on the first two
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Process.c b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Process.c
index ef1d9eb..56fb04f 100644
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Process.c
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Process.c
@@ -106,7 +106,7 @@
* The Concert Surround module carries out processing only on L, R.
*/
pInput = pScratch + (2 * NrFrames);
- pStIn = pScratch + (LVCS_SCRATCHBUFFERS * NrFrames);
+ pStIn = pScratch + ((LVCS_SCRATCHBUFFERS - 2) * NrFrames);
/* The first two channel data is extracted from the input data and
* copied into pInput buffer
*/
@@ -303,13 +303,45 @@
*/
if (pInstance->Params.OperatingMode != LVCS_OFF)
{
+#ifdef SUPPORT_MC
+ LVM_FLOAT *pStereoOut;
+ /*
+ * LVCS_Process_CS uses output buffer to store intermediate outputs of StereoEnhancer,
+ * Equalizer, ReverbGenerator and BypassMixer.
+ * So, to avoid i/o data overlapping, when i/o buffers are common, use scratch buffer
+ * to store intermediate outputs.
+ */
+ if (pOutData == pInData)
+ {
+ /*
+ * Scratch memory is used in 4 chunks of (2 * NrFrames) size.
+ * First chunk of memory is used by LVCS_StereoEnhancer and LVCS_ReverbGenerator,
+ * second and fourth are used as input buffers by pInput and pStIn in LVCS_Process_CS.
+ * Hence, pStereoOut is pointed to use unused third portion of scratch memory.
+ */
+ pStereoOut = (LVM_FLOAT *) \
+ pInstance->MemoryTable. \
+ Region[LVCS_MEMREGION_TEMPORARY_FAST].pBaseAddress +
+ ((LVCS_SCRATCHBUFFERS - 4) * NrFrames);
+ }
+ else
+ {
+ pStereoOut = pOutData;
+ }
+
/*
* Call CS process function
*/
err = LVCS_Process_CS(hInstance,
pInData,
+ pStereoOut,
+ NrFrames);
+#else
+ err = LVCS_Process_CS(hInstance,
+ pInData,
pOutData,
NumSamples);
+#endif
/*
@@ -329,10 +361,17 @@
if(NumSamples < LVCS_COMPGAINFRAME)
{
+#ifdef SUPPORT_MC
+ NonLinComp_Float(Gain, /* Compressor gain setting */
+ pStereoOut,
+ pStereoOut,
+ (LVM_INT32)(2 * NrFrames));
+#else
NonLinComp_Float(Gain, /* Compressor gain setting */
pOutData,
pOutData,
(LVM_INT32)(2 * NumSamples));
+#endif
}
else
{
@@ -361,7 +400,11 @@
FinalGain = Gain;
Gain = pInstance->CompressGain;
+#ifdef SUPPORT_MC
+ pOutPtr = pStereoOut;
+#else
pOutPtr = pOutData;
+#endif
while(SampleToProcess > 0)
{
@@ -428,6 +471,7 @@
}
#ifdef SUPPORT_MC
Copy_Float_Stereo_Mc(pInData,
+ pStereoOut,
pOutData,
NrFrames,
channels);
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index 10dda19..0a2850f 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -2710,7 +2710,7 @@
name[*pValueSize - 1] = 0;
*pValueSize = strlen(name) + 1;
ALOGVV("%s EQ_PARAM_GET_PRESET_NAME preset %d, name %s len %d",
- __func__, preset, gEqualizerPresets[preset].name, *pValueSize);
+ __func__, preset, name, *pValueSize);
} break;
diff --git a/media/libheif/HeifDecoderImpl.cpp b/media/libheif/HeifDecoderImpl.cpp
index bbc14a9..2208eca 100644
--- a/media/libheif/HeifDecoderImpl.cpp
+++ b/media/libheif/HeifDecoderImpl.cpp
@@ -21,16 +21,17 @@
#include <stdio.h>
+#include <android/IDataSource.h>
#include <binder/IMemory.h>
#include <binder/MemoryDealer.h>
#include <drm/drm_framework_common.h>
-#include <media/IDataSource.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>
#include <utils/RefBase.h>
+#include <vector>
HeifDecoder* createHeifDecoder() {
return new android::HeifDecoderImpl();
@@ -38,6 +39,22 @@
namespace android {
+void initFrameInfo(HeifFrameInfo *info, const VideoFrame *videoFrame) {
+ info->mWidth = videoFrame->mWidth;
+ info->mHeight = videoFrame->mHeight;
+ info->mRotationAngle = videoFrame->mRotationAngle;
+ info->mBytesPerPixel = videoFrame->mBytesPerPixel;
+ info->mDurationUs = videoFrame->mDurationUs;
+ if (videoFrame->mIccSize > 0) {
+ info->mIccData.assign(
+ videoFrame->getFlattenedIccData(),
+ videoFrame->getFlattenedIccData() + videoFrame->mIccSize);
+ } else {
+ // clear old Icc data if there is no Icc data.
+ info->mIccData.clear();
+ }
+}
+
/*
* HeifDataSource
*
@@ -153,7 +170,7 @@
// copy from cache if the request falls entirely in cache
if (offset + size <= mCachedOffset + mCachedSize) {
- memcpy(mMemory->pointer(), mCache.get() + offset - mCachedOffset, size);
+ memcpy(mMemory->unsecurePointer(), mCache.get() + offset - mCachedOffset, size);
return size;
}
@@ -251,7 +268,7 @@
if (bytesAvailable < (int64_t)size) {
size = bytesAvailable;
}
- memcpy(mMemory->pointer(), mCache.get() + offset - mCachedOffset, size);
+ memcpy(mMemory->unsecurePointer(), mCache.get() + offset - mCachedOffset, size);
return size;
}
@@ -290,11 +307,11 @@
// it's not, default to HAL_PIXEL_FORMAT_RGB_565.
mOutputColor(HAL_PIXEL_FORMAT_RGB_565),
mCurScanline(0),
- mWidth(0),
- mHeight(0),
+ mTotalScanline(0),
mFrameDecoded(false),
mHasImage(false),
mHasVideo(false),
+ mSequenceLength(0),
mAvailableLines(0),
mNumSlices(1),
mSliceHeight(0),
@@ -333,48 +350,103 @@
mHasImage = hasImage && !strcasecmp(hasImage, "yes");
mHasVideo = hasVideo && !strcasecmp(hasVideo, "yes");
- sp<IMemory> sharedMem;
+
+ HeifFrameInfo* defaultInfo = nullptr;
if (mHasImage) {
// image index < 0 to retrieve primary image
- sharedMem = mRetriever->getImageAtIndex(
+ sp<IMemory> sharedMem = mRetriever->getImageAtIndex(
-1, mOutputColor, true /*metaOnly*/);
- } else if (mHasVideo) {
- sharedMem = mRetriever->getFrameAtTime(0,
- MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC,
- mOutputColor, true /*metaOnly*/);
+
+ if (sharedMem == nullptr || sharedMem->unsecurePointer() == nullptr) {
+ ALOGE("init: videoFrame is a nullptr");
+ return false;
+ }
+
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ VideoFrame* videoFrame = static_cast<VideoFrame*>(sharedMem->unsecurePointer());
+
+ ALOGV("Image dimension %dx%d, display %dx%d, angle %d, iccSize %d",
+ videoFrame->mWidth,
+ videoFrame->mHeight,
+ videoFrame->mDisplayWidth,
+ videoFrame->mDisplayHeight,
+ videoFrame->mRotationAngle,
+ videoFrame->mIccSize);
+
+ initFrameInfo(&mImageInfo, videoFrame);
+
+ if (videoFrame->mTileHeight >= 512) {
+ // Try decoding in slices only if the image has tiles and is big enough.
+ mSliceHeight = videoFrame->mTileHeight;
+ ALOGV("mSliceHeight %u", mSliceHeight);
+ }
+
+ defaultInfo = &mImageInfo;
}
- if (sharedMem == nullptr || sharedMem->pointer() == nullptr) {
- ALOGE("getFrameAtTime: videoFrame is a nullptr");
+ if (mHasVideo) {
+ sp<IMemory> sharedMem = mRetriever->getFrameAtTime(0,
+ MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC,
+ mOutputColor, true /*metaOnly*/);
+
+ if (sharedMem == nullptr || sharedMem->unsecurePointer() == nullptr) {
+ ALOGE("init: videoFrame is a nullptr");
+ return false;
+ }
+
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ VideoFrame* videoFrame = static_cast<VideoFrame*>(
+ sharedMem->unsecurePointer());
+
+ ALOGV("Sequence dimension %dx%d, display %dx%d, angle %d, iccSize %d",
+ videoFrame->mWidth,
+ videoFrame->mHeight,
+ videoFrame->mDisplayWidth,
+ videoFrame->mDisplayHeight,
+ videoFrame->mRotationAngle,
+ videoFrame->mIccSize);
+
+ initFrameInfo(&mSequenceInfo, videoFrame);
+
+ mSequenceLength = atoi(mRetriever->extractMetadata(METADATA_KEY_VIDEO_FRAME_COUNT));
+
+ if (defaultInfo == nullptr) {
+ defaultInfo = &mSequenceInfo;
+ }
+ }
+
+ if (defaultInfo == nullptr) {
+ ALOGD("No valid image or sequence available");
return false;
}
- VideoFrame* videoFrame = static_cast<VideoFrame*>(sharedMem->pointer());
-
- ALOGV("Meta dimension %dx%d, display %dx%d, angle %d, iccSize %d",
- videoFrame->mWidth,
- videoFrame->mHeight,
- videoFrame->mDisplayWidth,
- videoFrame->mDisplayHeight,
- videoFrame->mRotationAngle,
- videoFrame->mIccSize);
-
if (frameInfo != nullptr) {
- frameInfo->set(
- videoFrame->mWidth,
- videoFrame->mHeight,
- videoFrame->mRotationAngle,
- videoFrame->mBytesPerPixel,
- videoFrame->mIccSize,
- videoFrame->getFlattenedIccData());
+ *frameInfo = *defaultInfo;
}
- mWidth = videoFrame->mWidth;
- mHeight = videoFrame->mHeight;
- if (mHasImage && videoFrame->mTileHeight >= 512 && mWidth >= 3000 && mHeight >= 2000 ) {
- // Try decoding in slices only if the image has tiles and is big enough.
- mSliceHeight = videoFrame->mTileHeight;
- mNumSlices = (videoFrame->mHeight + mSliceHeight - 1) / mSliceHeight;
- ALOGV("mSliceHeight %u, mNumSlices %zu", mSliceHeight, mNumSlices);
+
+ // default total scanline, this might change if decodeSequence() is used
+ mTotalScanline = defaultInfo->mHeight;
+
+ return true;
+}
+
+bool HeifDecoderImpl::getSequenceInfo(
+ HeifFrameInfo* frameInfo, size_t *frameCount) {
+ ALOGV("%s", __FUNCTION__);
+ if (!mHasVideo) {
+ return false;
+ }
+ if (frameInfo != nullptr) {
+ *frameInfo = mSequenceInfo;
+ }
+ if (frameCount != nullptr) {
+ *frameCount = mSequenceLength;
}
return true;
}
@@ -413,15 +485,15 @@
ALOGV("decodeAsync(): decoding slice %zu", i);
size_t top = i * mSliceHeight;
size_t bottom = (i + 1) * mSliceHeight;
- if (bottom > mHeight) {
- bottom = mHeight;
+ if (bottom > mImageInfo.mHeight) {
+ bottom = mImageInfo.mHeight;
}
sp<IMemory> frameMemory = mRetriever->getImageRectAtIndex(
- -1, mOutputColor, 0, top, mWidth, bottom);
+ -1, mOutputColor, 0, top, mImageInfo.mWidth, bottom);
{
Mutex::Autolock autolock(mLock);
- if (frameMemory == nullptr || frameMemory->pointer() == nullptr) {
+ if (frameMemory == nullptr || frameMemory->unsecurePointer() == nullptr) {
mAsyncDecodeDone = true;
mScanlineReady.signal();
break;
@@ -449,42 +521,48 @@
// See if we want to decode in slices to allow client to start
// scanline processing in parallel with decode. If this fails
// we fallback to decoding the full frame.
- if (mHasImage && mNumSlices > 1) {
- // get first slice and metadata
- sp<IMemory> frameMemory = mRetriever->getImageRectAtIndex(
- -1, mOutputColor, 0, 0, mWidth, mSliceHeight);
-
- if (frameMemory == nullptr || frameMemory->pointer() == nullptr) {
- ALOGE("decode: metadata is a nullptr");
- return false;
+ if (mHasImage) {
+ if (mSliceHeight >= 512 &&
+ mImageInfo.mWidth >= 3000 &&
+ mImageInfo.mHeight >= 2000 ) {
+ // Try decoding in slices only if the image has tiles and is big enough.
+ mNumSlices = (mImageInfo.mHeight + mSliceHeight - 1) / mSliceHeight;
+ ALOGV("mSliceHeight %u, mNumSlices %zu", mSliceHeight, mNumSlices);
}
- VideoFrame* videoFrame = static_cast<VideoFrame*>(frameMemory->pointer());
+ if (mNumSlices > 1) {
+ // get first slice and metadata
+ sp<IMemory> frameMemory = mRetriever->getImageRectAtIndex(
+ -1, mOutputColor, 0, 0, mImageInfo.mWidth, mSliceHeight);
- if (frameInfo != nullptr) {
- frameInfo->set(
- videoFrame->mWidth,
- videoFrame->mHeight,
- videoFrame->mRotationAngle,
- videoFrame->mBytesPerPixel,
- videoFrame->mIccSize,
- videoFrame->getFlattenedIccData());
+ if (frameMemory == nullptr || frameMemory->unsecurePointer() == nullptr) {
+ ALOGE("decode: metadata is a nullptr");
+ return false;
+ }
+
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ VideoFrame* videoFrame = static_cast<VideoFrame*>(frameMemory->unsecurePointer());
+
+ if (frameInfo != nullptr) {
+ initFrameInfo(frameInfo, videoFrame);
+ }
+ mFrameMemory = frameMemory;
+ mAvailableLines = mSliceHeight;
+ mThread = new DecodeThread(this);
+ if (mThread->run("HeifDecode", ANDROID_PRIORITY_FOREGROUND) == OK) {
+ mFrameDecoded = true;
+ return true;
+ }
+ // Fallback to decode without slicing
+ mThread.clear();
+ mNumSlices = 1;
+ mSliceHeight = 0;
+ mAvailableLines = 0;
+ mFrameMemory.clear();
}
-
- mFrameMemory = frameMemory;
- mAvailableLines = mSliceHeight;
- mThread = new DecodeThread(this);
- if (mThread->run("HeifDecode", ANDROID_PRIORITY_FOREGROUND) == OK) {
- mFrameDecoded = true;
- return true;
- }
-
- // Fallback to decode without slicing
- mThread.clear();
- mNumSlices = 1;
- mSliceHeight = 0;
- mAvailableLines = 0;
- mFrameMemory.clear();
}
if (mHasImage) {
@@ -495,12 +573,16 @@
MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC, mOutputColor);
}
- if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
+ if (mFrameMemory == nullptr || mFrameMemory->unsecurePointer() == nullptr) {
ALOGE("decode: videoFrame is a nullptr");
return false;
}
- VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->unsecurePointer());
if (videoFrame->mSize == 0 ||
mFrameMemory->size() < videoFrame->getFlattenedSize()) {
ALOGE("decode: videoFrame size is invalid");
@@ -517,13 +599,8 @@
videoFrame->mSize);
if (frameInfo != nullptr) {
- frameInfo->set(
- videoFrame->mWidth,
- videoFrame->mHeight,
- videoFrame->mRotationAngle,
- videoFrame->mBytesPerPixel,
- videoFrame->mIccSize,
- videoFrame->getFlattenedIccData());
+ initFrameInfo(frameInfo, videoFrame);
+
}
mFrameDecoded = true;
@@ -533,18 +610,70 @@
return true;
}
-bool HeifDecoderImpl::getScanlineInner(uint8_t* dst) {
- if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
+bool HeifDecoderImpl::decodeSequence(int frameIndex, HeifFrameInfo* frameInfo) {
+ ALOGV("%s: frame index %d", __FUNCTION__, frameIndex);
+ if (!mHasVideo) {
return false;
}
- VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
+
+ if (frameIndex < 0 || frameIndex >= mSequenceLength) {
+ ALOGE("invalid frame index: %d, total frames %zu", frameIndex, mSequenceLength);
+ return false;
+ }
+
+ mCurScanline = 0;
+
+ // set total scanline to sequence height now
+ mTotalScanline = mSequenceInfo.mHeight;
+
+ mFrameMemory = mRetriever->getFrameAtIndex(frameIndex, mOutputColor);
+ if (mFrameMemory == nullptr || mFrameMemory->unsecurePointer() == nullptr) {
+ ALOGE("decode: videoFrame is a nullptr");
+ return false;
+ }
+
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->unsecurePointer());
+ if (videoFrame->mSize == 0 ||
+ mFrameMemory->size() < videoFrame->getFlattenedSize()) {
+ ALOGE("decode: videoFrame size is invalid");
+ return false;
+ }
+
+ ALOGV("Decoded dimension %dx%d, display %dx%d, angle %d, rowbytes %d, size %d",
+ videoFrame->mWidth,
+ videoFrame->mHeight,
+ videoFrame->mDisplayWidth,
+ videoFrame->mDisplayHeight,
+ videoFrame->mRotationAngle,
+ videoFrame->mRowBytes,
+ videoFrame->mSize);
+
+ if (frameInfo != nullptr) {
+ initFrameInfo(frameInfo, videoFrame);
+ }
+ return true;
+}
+
+bool HeifDecoderImpl::getScanlineInner(uint8_t* dst) {
+ if (mFrameMemory == nullptr || mFrameMemory->unsecurePointer() == nullptr) {
+ return false;
+ }
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->unsecurePointer());
uint8_t* src = videoFrame->getFlattenedData() + videoFrame->mRowBytes * mCurScanline++;
memcpy(dst, src, videoFrame->mBytesPerPixel * videoFrame->mWidth);
return true;
}
bool HeifDecoderImpl::getScanline(uint8_t* dst) {
- if (mCurScanline >= mHeight) {
+ if (mCurScanline >= mTotalScanline) {
ALOGE("no more scanline available");
return false;
}
@@ -564,8 +693,8 @@
size_t HeifDecoderImpl::skipScanlines(size_t count) {
uint32_t oldScanline = mCurScanline;
mCurScanline += count;
- if (mCurScanline > mHeight) {
- mCurScanline = mHeight;
+ if (mCurScanline > mTotalScanline) {
+ mCurScanline = mTotalScanline;
}
return (mCurScanline > oldScanline) ? (mCurScanline - oldScanline) : 0;
}
diff --git a/media/libheif/HeifDecoderImpl.h b/media/libheif/HeifDecoderImpl.h
index 528ee3b..69c74a7 100644
--- a/media/libheif/HeifDecoderImpl.h
+++ b/media/libheif/HeifDecoderImpl.h
@@ -40,12 +40,16 @@
bool init(HeifStream* stream, HeifFrameInfo* frameInfo) override;
+ bool getSequenceInfo(HeifFrameInfo* frameInfo, size_t *frameCount) override;
+
bool getEncodedColor(HeifEncodedColor* outColor) const override;
bool setOutputColor(HeifColorFormat heifColor) override;
bool decode(HeifFrameInfo* frameInfo) override;
+ bool decodeSequence(int frameIndex, HeifFrameInfo* frameInfo) override;
+
bool getScanline(uint8_t* dst) override;
size_t skipScanlines(size_t count) override;
@@ -56,13 +60,15 @@
sp<IDataSource> mDataSource;
sp<MediaMetadataRetriever> mRetriever;
sp<IMemory> mFrameMemory;
+ HeifFrameInfo mImageInfo;
+ HeifFrameInfo mSequenceInfo;
android_pixel_format_t mOutputColor;
size_t mCurScanline;
- uint32_t mWidth;
- uint32_t mHeight;
+ size_t mTotalScanline;
bool mFrameDecoded;
bool mHasImage;
bool mHasVideo;
+ size_t mSequenceLength;
// Slice decoding only
Mutex mLock;
diff --git a/media/libheif/include/HeifDecoderAPI.h b/media/libheif/include/HeifDecoderAPI.h
index aa10f33..9073672 100644
--- a/media/libheif/include/HeifDecoderAPI.h
+++ b/media/libheif/include/HeifDecoderAPI.h
@@ -17,7 +17,7 @@
#ifndef _HEIF_DECODER_API_
#define _HEIF_DECODER_API_
-#include <memory>
+#include <vector>
/*
* The output color pixel format of heif decoder.
@@ -40,41 +40,13 @@
/*
* Represents a color converted (RGB-based) video frame
*/
-struct HeifFrameInfo
-{
- HeifFrameInfo() :
- mWidth(0), mHeight(0), mRotationAngle(0), mBytesPerPixel(0),
- mIccSize(0), mIccData(nullptr) {}
-
- // update the frame info, will make a copy of |iccData| internally
- void set(uint32_t width, uint32_t height, int32_t rotation, uint32_t bpp,
- uint32_t iccSize, uint8_t* iccData) {
- mWidth = width;
- mHeight = height;
- mRotationAngle = rotation;
- mBytesPerPixel = bpp;
-
- if (mIccData != nullptr) {
- mIccData.reset(nullptr);
- }
- mIccSize = iccSize;
- if (iccSize > 0) {
- mIccData.reset(new uint8_t[iccSize]);
- if (mIccData.get() != nullptr) {
- memcpy(mIccData.get(), iccData, iccSize);
- } else {
- mIccSize = 0;
- }
- }
- }
-
- // Intentional public access modifiers:
+struct HeifFrameInfo {
uint32_t mWidth;
uint32_t mHeight;
int32_t mRotationAngle; // Rotation angle, clockwise, should be multiple of 90
uint32_t mBytesPerPixel; // Number of bytes for one pixel
- uint32_t mIccSize; // Number of bytes in mIccData
- std::unique_ptr<uint8_t[]> mIccData; // Actual ICC data, memory is owned by this structure
+ int64_t mDurationUs; // Duration of the frame in us
+ std::vector<uint8_t> mIccData; // ICC data array
};
/*
@@ -113,8 +85,8 @@
virtual size_t getLength() const = 0;
private:
- HeifStream(const HeifFrameInfo&) = delete;
- HeifStream& operator=(const HeifFrameInfo&) = delete;
+ HeifStream(const HeifStream&) = delete;
+ HeifStream& operator=(const HeifStream&) = delete;
};
/*
@@ -146,6 +118,14 @@
virtual bool init(HeifStream* stream, HeifFrameInfo* frameInfo) = 0;
/*
+ * Returns true if the stream contains an image sequence and false otherwise.
+ * |frameInfo| will be filled with information of pictures in the sequence
+ * and |frameCount| the length of the sequence upon success and unmodified
+ * upon failure.
+ */
+ virtual bool getSequenceInfo(HeifFrameInfo* frameInfo, size_t *frameCount) = 0;
+
+ /*
* Decode the picture internally, returning whether it succeeded. |frameInfo|
* will be filled with information of the primary picture upon success and
* unmodified upon failure.
@@ -156,6 +136,20 @@
virtual bool decode(HeifFrameInfo* frameInfo) = 0;
/*
+ * Decode the picture from the image sequence at index |frameIndex|.
+ * |frameInfo| will be filled with information of the decoded picture upon
+ * success and unmodified upon failure.
+ *
+ * |frameIndex| is the 0-based index of the video frame to retrieve. The frame
+ * index must be that of a valid frame. The total number of frames available for
+ * retrieval was reported via getSequenceInfo().
+ *
+ * After this succeeded, getScanline can be called to read the scanlines
+ * that were decoded.
+ */
+ virtual bool decodeSequence(int frameIndex, HeifFrameInfo* frameInfo) = 0;
+
+ /*
* Read the next scanline (in top-down order), returns true upon success
* and false otherwise.
*/
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index 778ee44..e8ff463 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -2,7 +2,7 @@
name: "libmedia_headers",
vendor_available: true,
export_include_dirs: ["include"],
- header_libs:[
+ header_libs: [
"libbase_headers",
"libgui_headers",
"libstagefright_headers",
@@ -18,12 +18,33 @@
filegroup {
name: "libmedia_omx_aidl",
srcs: [
- "aidl/android/IGraphicBufferSource.aidl",
"aidl/android/IOMXBufferSource.aidl",
],
path: "aidl",
}
+filegroup {
+ name: "mediaextractorservice_aidl",
+ srcs: [
+ "aidl/android/IMediaExtractorService.aidl",
+ ],
+ path: "aidl",
+}
+
+aidl_interface {
+ name: "resourcemanager_aidl_interface",
+ local_include_dir: "aidl",
+ srcs: [
+ "aidl/android/media/IResourceManagerClient.aidl",
+ "aidl/android/media/IResourceManagerService.aidl",
+ "aidl/android/media/MediaResourceType.aidl",
+ "aidl/android/media/MediaResourceSubType.aidl",
+ "aidl/android/media/MediaResourceParcel.aidl",
+ "aidl/android/media/MediaResourcePolicyParcel.aidl",
+ ],
+ versions: ["1"],
+}
+
cc_library_shared {
name: "libmedia_omx",
vendor_available: true,
@@ -38,7 +59,6 @@
"IOMX.cpp",
"MediaCodecBuffer.cpp",
"OMXBuffer.cpp",
- "omx/1.0/WGraphicBufferSource.cpp",
"omx/1.0/WOmxBufferSource.cpp",
"omx/1.0/WOmxNode.cpp",
"omx/1.0/WOmxObserver.cpp",
@@ -102,7 +122,6 @@
},
}
-
cc_library_shared {
name: "libmedia_omx_client",
@@ -238,13 +257,13 @@
name: "libmedia",
srcs: [
+ ":mediaextractorservice_aidl",
"IDataSource.cpp",
"BufferingSettings.cpp",
"mediaplayer.cpp",
"IMediaHTTPConnection.cpp",
"IMediaHTTPService.cpp",
"IMediaExtractor.cpp",
- "IMediaExtractorService.cpp",
"IMediaPlayerService.cpp",
"IMediaPlayerClient.cpp",
"IMediaRecorderClient.cpp",
@@ -253,8 +272,6 @@
"IMediaSource.cpp",
"IRemoteDisplay.cpp",
"IRemoteDisplayClient.cpp",
- "IResourceManagerClient.cpp",
- "IResourceManagerService.cpp",
"IStreamSource.cpp",
"MediaUtils.cpp",
"Metadata.cpp",
@@ -296,6 +313,7 @@
"libprocessgroup",
"libutils",
"libbinder",
+ "libbinder_ndk",
"libsonivox",
"libandroidicu",
"libexpat",
@@ -317,7 +335,12 @@
],
static_libs: [
- "libc_malloc_debug_backtrace", // for memory heap analysis
+ "libc_malloc_debug_backtrace", // for memory heap analysis
+ "resourcemanager_aidl_interface-ndk_platform",
+ ],
+
+ export_static_lib_headers: [
+ "resourcemanager_aidl_interface-ndk_platform",
],
export_include_dirs: [
diff --git a/media/libmedia/IDataSource.cpp b/media/libmedia/IDataSource.cpp
index 61f0a68..e96a113 100644
--- a/media/libmedia/IDataSource.cpp
+++ b/media/libmedia/IDataSource.cpp
@@ -19,8 +19,7 @@
#include <utils/Log.h>
#include <utils/Timers.h>
-#include <media/IDataSource.h>
-
+#include <android/IDataSource.h>
#include <binder/IMemory.h>
#include <binder/Parcel.h>
#include <media/stagefright/foundation/ADebug.h>
diff --git a/media/libmedia/IMediaExtractor.cpp b/media/libmedia/IMediaExtractor.cpp
index fb6d3a2..7389851 100644
--- a/media/libmedia/IMediaExtractor.cpp
+++ b/media/libmedia/IMediaExtractor.cpp
@@ -25,7 +25,7 @@
#include <binder/IPCThreadState.h>
#include <binder/Parcel.h>
#include <binder/PermissionCache.h>
-#include <media/IMediaExtractor.h>
+#include <android/IMediaExtractor.h>
#include <media/stagefright/MetaData.h>
namespace android {
diff --git a/media/libmedia/IMediaExtractorService.cpp b/media/libmedia/IMediaExtractorService.cpp
deleted file mode 100644
index 243b09d..0000000
--- a/media/libmedia/IMediaExtractorService.cpp
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
-**
-** Copyright 2007, 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 "IMediaExtractorService"
-//#define LOG_NDEBUG 0
-
-#include <utils/Log.h>
-#include <stdint.h>
-#include <sys/types.h>
-#include <binder/Parcel.h>
-#include <media/IMediaExtractorService.h>
-
-namespace android {
-
-enum {
- MAKE_EXTRACTOR = IBinder::FIRST_CALL_TRANSACTION,
- MAKE_IDATA_SOURCE_FD,
- GET_SUPPORTED_TYPES,
-};
-
-class BpMediaExtractorService : public BpInterface<IMediaExtractorService>
-{
-public:
- explicit BpMediaExtractorService(const sp<IBinder>& impl)
- : BpInterface<IMediaExtractorService>(impl)
- {
- }
-
- virtual sp<IMediaExtractor> makeExtractor(const sp<IDataSource> &source, const char *mime) {
- Parcel data, reply;
- data.writeInterfaceToken(IMediaExtractorService::getInterfaceDescriptor());
- data.writeStrongBinder(IInterface::asBinder(source));
- if (mime != NULL) {
- data.writeCString(mime);
- }
- status_t ret = remote()->transact(MAKE_EXTRACTOR, data, &reply);
- if (ret == NO_ERROR) {
- return interface_cast<IMediaExtractor>(reply.readStrongBinder());
- }
- return NULL;
- }
-
- virtual sp<IDataSource> makeIDataSource(int fd, int64_t offset, int64_t length)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IMediaExtractorService::getInterfaceDescriptor());
- data.writeFileDescriptor(fd);
- data.writeInt64(offset);
- data.writeInt64(length);
- status_t ret = remote()->transact(MAKE_IDATA_SOURCE_FD, data, &reply);
- ALOGV("fd:%d offset:%lld length:%lld ret:%d",
- fd, (long long)offset, (long long)length, ret);
- if (ret == NO_ERROR) {
- return interface_cast<IDataSource>(reply.readStrongBinder());
- }
- return nullptr;
- }
-
- virtual std::unordered_set<std::string> getSupportedTypes() {
- std::unordered_set<std::string> supportedTypes;
- Parcel data, reply;
- data.writeInterfaceToken(IMediaExtractorService::getInterfaceDescriptor());
- status_t ret = remote()->transact(GET_SUPPORTED_TYPES, data, &reply);
- if (ret == NO_ERROR) {
- // process reply
- while(true) {
- const char *ext = reply.readCString();
- if (!ext) {
- break;
- }
- supportedTypes.insert(std::string(ext));
- }
- }
- return supportedTypes;
- }
-};
-
-IMPLEMENT_META_INTERFACE(MediaExtractorService, "android.media.IMediaExtractorService");
-
-// ----------------------------------------------------------------------
-
-status_t BnMediaExtractorService::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch (code) {
-
- case MAKE_EXTRACTOR: {
- CHECK_INTERFACE(IMediaExtractorService, data, reply);
- sp<IBinder> b;
- status_t ret = data.readStrongBinder(&b);
- if (ret != NO_ERROR || b == NULL) {
- ALOGE("Error reading source from parcel");
- return ret;
- }
- // If we make an extractor through Binder, enabled shared memory
- // for MediaBuffers for this process.
- MediaBuffer::useSharedMemory();
- sp<IDataSource> source = interface_cast<IDataSource>(b);
- const char *mime = data.readCString();
- sp<IMediaExtractor> ex = makeExtractor(source, mime);
- reply->writeStrongBinder(IInterface::asBinder(ex));
- return NO_ERROR;
- }
-
- case MAKE_IDATA_SOURCE_FD: {
- CHECK_INTERFACE(IMediaExtractorService, data, reply);
- const int fd = dup(data.readFileDescriptor()); // -1 fd checked in makeIDataSource
- const int64_t offset = data.readInt64();
- const int64_t length = data.readInt64();
- ALOGV("fd %d offset%lld length:%lld", fd, (long long)offset, (long long)length);
- sp<IDataSource> source = makeIDataSource(fd, offset, length);
- reply->writeStrongBinder(IInterface::asBinder(source));
- // The FileSource closes the descriptor, so if it is not created
- // we need to close the descriptor explicitly.
- if (source.get() == nullptr && fd != -1) {
- close(fd);
- }
- return NO_ERROR;
- }
-
- case GET_SUPPORTED_TYPES:
- {
- CHECK_INTERFACE(IMediaExtractorService, data, reply);
- std::unordered_set<std::string> supportedTypes = getSupportedTypes();
- for (auto it = supportedTypes.begin(); it != supportedTypes.end(); ++it) {
- reply->writeCString((*it).c_str());
- }
- return NO_ERROR;
- }
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-// ----------------------------------------------------------------------------
-
-} // namespace android
diff --git a/media/libmedia/IMediaHTTPConnection.cpp b/media/libmedia/IMediaHTTPConnection.cpp
index 1bb8d67..8cbb4c2 100644
--- a/media/libmedia/IMediaHTTPConnection.cpp
+++ b/media/libmedia/IMediaHTTPConnection.cpp
@@ -128,12 +128,12 @@
ALOGE("readAt got a NULL buffer");
return UNKNOWN_ERROR;
}
- if (mMemory->pointer() == NULL) {
- ALOGE("readAt got a NULL mMemory->pointer()");
+ if (mMemory->unsecurePointer() == NULL) {
+ ALOGE("readAt got a NULL mMemory->unsecurePointer()");
return UNKNOWN_ERROR;
}
- memcpy(buffer, mMemory->pointer(), len);
+ memcpy(buffer, mMemory->unsecurePointer(), len);
return len;
}
diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp
index f9fa86e..8a3b84e 100644
--- a/media/libmedia/IMediaMetadataRetriever.cpp
+++ b/media/libmedia/IMediaMetadataRetriever.cpp
@@ -19,8 +19,8 @@
#include <stdint.h>
#include <sys/types.h>
+#include <android/IDataSource.h>
#include <binder/Parcel.h>
-#include <media/IDataSource.h>
#include <media/IMediaHTTPService.h>
#include <media/IMediaMetadataRetriever.h>
#include <processgroup/sched_policy.h>
@@ -109,7 +109,7 @@
data.writeInt32(0);
} else {
// serialize the headers
- data.writeInt64(headers->size());
+ data.writeInt32(headers->size());
for (size_t i = 0; i < headers->size(); ++i) {
data.writeString8(headers->keyAt(i));
data.writeString8(headers->valueAt(i));
@@ -213,15 +213,14 @@
return interface_cast<IMemory>(reply.readStrongBinder());
}
- status_t getFrameAtIndex(std::vector<sp<IMemory> > *frames,
- int frameIndex, int numFrames, int colorFormat, bool metaOnly)
+ sp<IMemory> getFrameAtIndex(
+ int index, int colorFormat, bool metaOnly)
{
- ALOGV("getFrameAtIndex: frameIndex(%d), numFrames(%d), colorFormat(%d) metaOnly(%d)",
- frameIndex, numFrames, colorFormat, metaOnly);
+ ALOGV("getFrameAtIndex: index(%d), colorFormat(%d) metaOnly(%d)",
+ index, colorFormat, metaOnly);
Parcel data, reply;
data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
- data.writeInt32(frameIndex);
- data.writeInt32(numFrames);
+ data.writeInt32(index);
data.writeInt32(colorFormat);
data.writeInt32(metaOnly);
#ifndef DISABLE_GROUP_SCHEDULE_HACK
@@ -230,16 +229,9 @@
remote()->transact(GET_FRAME_AT_INDEX, data, &reply);
status_t ret = reply.readInt32();
if (ret != NO_ERROR) {
- return ret;
+ return NULL;
}
- int retNumFrames = reply.readInt32();
- if (retNumFrames < numFrames) {
- numFrames = retNumFrames;
- }
- for (int i = 0; i < numFrames; i++) {
- frames->push_back(interface_cast<IMemory>(reply.readStrongBinder()));
- }
- return OK;
+ return interface_cast<IMemory>(reply.readStrongBinder());
}
sp<IMemory> extractAlbumArt()
@@ -318,11 +310,22 @@
}
KeyedVector<String8, String8> headers;
- size_t numHeaders = (size_t) data.readInt64();
+ size_t numHeaders = (size_t) data.readInt32();
for (size_t i = 0; i < numHeaders; ++i) {
- String8 key = data.readString8();
- String8 value = data.readString8();
- headers.add(key, value);
+ String8 key;
+ String8 value;
+ status_t status;
+ status = data.readString8(&key);
+ if (status != OK) {
+ return status;
+ }
+ status = data.readString8(&value);
+ if (status != OK) {
+ return status;
+ }
+ if (headers.add(key, value) < 0) {
+ return UNKNOWN_ERROR;
+ }
}
reply->writeInt32(
@@ -431,24 +434,20 @@
case GET_FRAME_AT_INDEX: {
CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
- int frameIndex = data.readInt32();
- int numFrames = data.readInt32();
+ int index = data.readInt32();
int colorFormat = data.readInt32();
bool metaOnly = (data.readInt32() != 0);
- ALOGV("getFrameAtIndex: frameIndex(%d), numFrames(%d), colorFormat(%d), metaOnly(%d)",
- frameIndex, numFrames, colorFormat, metaOnly);
+ ALOGV("getFrameAtIndex: index(%d), colorFormat(%d), metaOnly(%d)",
+ index, colorFormat, metaOnly);
#ifndef DISABLE_GROUP_SCHEDULE_HACK
setSchedPolicy(data);
#endif
- std::vector<sp<IMemory> > frames;
- status_t err = getFrameAtIndex(
- &frames, frameIndex, numFrames, colorFormat, metaOnly);
- reply->writeInt32(err);
- if (OK == err) {
- reply->writeInt32(frames.size());
- for (size_t i = 0; i < frames.size(); i++) {
- reply->writeStrongBinder(IInterface::asBinder(frames[i]));
- }
+ sp<IMemory> frame = getFrameAtIndex(index, colorFormat, metaOnly);
+ if (frame != nullptr) { // Don't send NULL across the binder interface
+ reply->writeInt32(NO_ERROR);
+ reply->writeStrongBinder(IInterface::asBinder(frame));
+ } else {
+ reply->writeInt32(UNKNOWN_ERROR);
}
#ifndef DISABLE_GROUP_SCHEDULE_HACK
restoreSchedPolicy();
diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp
index ea06665..20bc23d 100644
--- a/media/libmedia/IMediaPlayer.cpp
+++ b/media/libmedia/IMediaPlayer.cpp
@@ -19,18 +19,15 @@
#include <stdint.h>
#include <sys/types.h>
+#include <android/IDataSource.h>
#include <binder/Parcel.h>
-
+#include <gui/IGraphicBufferProducer.h>
#include <media/AudioResamplerPublic.h>
#include <media/AVSyncSettings.h>
#include <media/BufferingSettings.h>
-
-#include <media/IDataSource.h>
#include <media/IMediaHTTPService.h>
#include <media/IMediaPlayer.h>
#include <media/IStreamSource.h>
-
-#include <gui/IGraphicBufferProducer.h>
#include <utils/String8.h>
namespace android {
diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp
index a354ce1..ac86f72 100644
--- a/media/libmedia/IMediaRecorder.cpp
+++ b/media/libmedia/IMediaRecorder.cpp
@@ -67,7 +67,9 @@
GET_ACTIVE_MICROPHONES,
GET_PORT_ID,
SET_PREFERRED_MICROPHONE_DIRECTION,
- SET_PREFERRED_MICROPHONE_FIELD_DIMENSION
+ SET_PREFERRED_MICROPHONE_FIELD_DIMENSION,
+ SET_PRIVACY_SENSITIVE,
+ GET_PRIVACY_SENSITIVE
};
class BpMediaRecorder: public BpInterface<IMediaRecorder>
@@ -151,6 +153,36 @@
return reply.readInt32();
}
+ status_t setPrivacySensitive(bool privacySensitive)
+ {
+ ALOGV("%s(%s)", __func__, privacySensitive ? "true" : "false");
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor());
+ data.writeInt32(privacySensitive ? 1 : 0);
+ status_t status = remote()->transact(SET_PRIVACY_SENSITIVE, data, &reply);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ return reply.readInt32();
+ }
+
+ status_t isPrivacySensitive(bool *privacySensitive) const
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor());
+ *privacySensitive = false;
+ status_t status = remote()->transact(GET_PRIVACY_SENSITIVE, data, &reply);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ status = reply.readInt32();
+ if (status == NO_ERROR) {
+ *privacySensitive = reply.readInt32() == 1;
+ }
+ ALOGV("%s status %d enabled: %s", __func__, status, *privacySensitive ? "true" : "false");
+ return status;
+ }
+
status_t setOutputFormat(int of)
{
ALOGV("setOutputFormat(%d)", of);
@@ -537,6 +569,24 @@
reply->writeInt32(setAudioSource(as));
return NO_ERROR;
} break;
+ case SET_PRIVACY_SENSITIVE: {
+ ALOGV("SET_PRIVACY_SENSITIVE");
+ CHECK_INTERFACE(IMediaRecorder, data, reply);
+ bool privacySensitive = data.readInt32() == 1;
+ reply->writeInt32(setPrivacySensitive(privacySensitive));
+ return NO_ERROR;
+ } break;
+ case GET_PRIVACY_SENSITIVE: {
+ ALOGV("GET_PRIVACY_SENSITIVE");
+ CHECK_INTERFACE(IMediaRecorder, data, reply);
+ bool privacySensitive = false;
+ status_t status = isPrivacySensitive(&privacySensitive);
+ reply->writeInt32(status);
+ if (status == NO_ERROR) {
+ reply->writeInt32(privacySensitive ? 1 : 0);
+ }
+ return NO_ERROR;
+ } break;
case SET_OUTPUT_FORMAT: {
ALOGV("SET_OUTPUT_FORMAT");
CHECK_INTERFACE(IMediaRecorder, data, reply);
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/IOMX.cpp b/media/libmedia/IOMX.cpp
index bc0c2cd..959a3d7 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -29,7 +29,6 @@
#include <utils/NativeHandle.h>
#include <media/omx/1.0/WOmxNode.h>
-#include <android/IGraphicBufferSource.h>
#include <android/IOMXBufferSource.h>
namespace android {
diff --git a/media/libmedia/IResourceManagerClient.cpp b/media/libmedia/IResourceManagerClient.cpp
deleted file mode 100644
index 1fea479..0000000
--- a/media/libmedia/IResourceManagerClient.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
-**
-** 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.
-*/
-
-#include <utils/RefBase.h>
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-
-#include <media/IResourceManagerClient.h>
-
-namespace android {
-
-enum {
- RECLAIM_RESOURCE = IBinder::FIRST_CALL_TRANSACTION,
- GET_NAME,
-};
-
-class BpResourceManagerClient: public BpInterface<IResourceManagerClient>
-{
-public:
- explicit BpResourceManagerClient(const sp<IBinder> &impl)
- : BpInterface<IResourceManagerClient>(impl)
- {
- }
-
- virtual bool reclaimResource() {
- Parcel data, reply;
- data.writeInterfaceToken(IResourceManagerClient::getInterfaceDescriptor());
-
- bool ret = false;
- status_t status = remote()->transact(RECLAIM_RESOURCE, data, &reply);
- if (status == NO_ERROR) {
- ret = (bool)reply.readInt32();
- }
- return ret;
- }
-
- virtual String8 getName() {
- Parcel data, reply;
- data.writeInterfaceToken(IResourceManagerClient::getInterfaceDescriptor());
-
- String8 ret;
- status_t status = remote()->transact(GET_NAME, data, &reply);
- if (status == NO_ERROR) {
- ret = reply.readString8();
- }
- return ret;
- }
-
-};
-
-IMPLEMENT_META_INTERFACE(ResourceManagerClient, "android.media.IResourceManagerClient");
-
-// ----------------------------------------------------------------------
-
-status_t BnResourceManagerClient::onTransact(
- uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags)
-{
- switch (code) {
- case RECLAIM_RESOURCE: {
- CHECK_INTERFACE(IResourceManagerClient, data, reply);
- bool ret = reclaimResource();
- reply->writeInt32(ret);
- return NO_ERROR;
- } break;
- case GET_NAME: {
- CHECK_INTERFACE(IResourceManagerClient, data, reply);
- String8 ret = getName();
- reply->writeString8(ret);
- return NO_ERROR;
- } break;
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-}; // namespace android
diff --git a/media/libmedia/IResourceManagerService.cpp b/media/libmedia/IResourceManagerService.cpp
deleted file mode 100644
index f8a0a14..0000000
--- a/media/libmedia/IResourceManagerService.cpp
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
-**
-** 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.
-*/
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "IResourceManagerService"
-#include <utils/Log.h>
-
-#include <media/IResourceManagerService.h>
-
-#include <binder/Parcel.h>
-
-#include <stdint.h>
-#include <sys/types.h>
-
-namespace android {
-
-enum {
- CONFIG = IBinder::FIRST_CALL_TRANSACTION,
- ADD_RESOURCE,
- REMOVE_RESOURCE,
- REMOVE_CLIENT,
- RECLAIM_RESOURCE,
-};
-
-template <typename T>
-static void writeToParcel(Parcel *data, const Vector<T> &items) {
- size_t size = items.size();
- // truncates size, but should be okay for this usecase
- data->writeUint32(static_cast<uint32_t>(size));
- for (size_t i = 0; i < size; i++) {
- items[i].writeToParcel(data);
- }
-}
-
-template <typename T>
-static void readFromParcel(const Parcel &data, Vector<T> *items) {
- size_t size = (size_t)data.readUint32();
- for (size_t i = 0; i < size && data.dataAvail() > 0; i++) {
- T item;
- item.readFromParcel(data);
- items->add(item);
- }
-}
-
-class BpResourceManagerService : public BpInterface<IResourceManagerService>
-{
-public:
- explicit BpResourceManagerService(const sp<IBinder> &impl)
- : BpInterface<IResourceManagerService>(impl)
- {
- }
-
- virtual void config(const Vector<MediaResourcePolicy> &policies) {
- Parcel data, reply;
- data.writeInterfaceToken(IResourceManagerService::getInterfaceDescriptor());
- writeToParcel(&data, policies);
- remote()->transact(CONFIG, data, &reply);
- }
-
- virtual void addResource(
- int pid,
- int uid,
- int64_t clientId,
- const sp<IResourceManagerClient> client,
- const Vector<MediaResource> &resources) {
- Parcel data, reply;
- data.writeInterfaceToken(IResourceManagerService::getInterfaceDescriptor());
- data.writeInt32(pid);
- data.writeInt32(uid);
- data.writeInt64(clientId);
- data.writeStrongBinder(IInterface::asBinder(client));
- writeToParcel(&data, resources);
-
- remote()->transact(ADD_RESOURCE, data, &reply);
- }
-
- virtual void removeResource(int pid, int64_t clientId, const Vector<MediaResource> &resources) {
- Parcel data, reply;
- data.writeInterfaceToken(IResourceManagerService::getInterfaceDescriptor());
- data.writeInt32(pid);
- data.writeInt64(clientId);
- writeToParcel(&data, resources);
-
- remote()->transact(REMOVE_RESOURCE, data, &reply);
- }
-
- virtual void removeClient(int pid, int64_t clientId) {
- Parcel data, reply;
- data.writeInterfaceToken(IResourceManagerService::getInterfaceDescriptor());
- data.writeInt32(pid);
- data.writeInt64(clientId);
-
- remote()->transact(REMOVE_CLIENT, data, &reply);
- }
-
- virtual bool reclaimResource(int callingPid, const Vector<MediaResource> &resources) {
- Parcel data, reply;
- data.writeInterfaceToken(IResourceManagerService::getInterfaceDescriptor());
- data.writeInt32(callingPid);
- writeToParcel(&data, resources);
-
- bool ret = false;
- status_t status = remote()->transact(RECLAIM_RESOURCE, data, &reply);
- if (status == NO_ERROR) {
- ret = (bool)reply.readInt32();
- }
- return ret;
- }
-};
-
-IMPLEMENT_META_INTERFACE(ResourceManagerService, "android.media.IResourceManagerService");
-
-// ----------------------------------------------------------------------
-
-
-status_t BnResourceManagerService::onTransact(
- uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags)
-{
- switch (code) {
- case CONFIG: {
- CHECK_INTERFACE(IResourceManagerService, data, reply);
- Vector<MediaResourcePolicy> policies;
- readFromParcel(data, &policies);
- config(policies);
- return NO_ERROR;
- } break;
-
- case ADD_RESOURCE: {
- CHECK_INTERFACE(IResourceManagerService, data, reply);
- int pid = data.readInt32();
- int uid = data.readInt32();
- int64_t clientId = data.readInt64();
- sp<IResourceManagerClient> client(
- interface_cast<IResourceManagerClient>(data.readStrongBinder()));
- if (client == NULL) {
- return NO_ERROR;
- }
- Vector<MediaResource> resources;
- readFromParcel(data, &resources);
- addResource(pid, uid, clientId, client, resources);
- return NO_ERROR;
- } break;
-
- case REMOVE_RESOURCE: {
- CHECK_INTERFACE(IResourceManagerService, data, reply);
- int pid = data.readInt32();
- int64_t clientId = data.readInt64();
- Vector<MediaResource> resources;
- readFromParcel(data, &resources);
- removeResource(pid, clientId, resources);
- return NO_ERROR;
- } break;
-
- case REMOVE_CLIENT: {
- CHECK_INTERFACE(IResourceManagerService, data, reply);
- int pid = data.readInt32();
- int64_t clientId = data.readInt64();
- removeClient(pid, clientId);
- return NO_ERROR;
- } break;
-
- case RECLAIM_RESOURCE: {
- CHECK_INTERFACE(IResourceManagerService, data, reply);
- int callingPid = data.readInt32();
- Vector<MediaResource> resources;
- readFromParcel(data, &resources);
- bool ret = reclaimResource(callingPid, resources);
- reply->writeInt32(ret);
- return NO_ERROR;
- } break;
-
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
diff --git a/media/libmedia/MediaResource.cpp b/media/libmedia/MediaResource.cpp
index 8626009..0936a99 100644
--- a/media/libmedia/MediaResource.cpp
+++ b/media/libmedia/MediaResource.cpp
@@ -23,42 +23,54 @@
namespace android {
-MediaResource::MediaResource()
- : mType(kUnspecified),
- mSubType(kUnspecifiedSubType),
- mValue(0) {}
-
-MediaResource::MediaResource(Type type, uint64_t value)
- : mType(type),
- mSubType(kUnspecifiedSubType),
- mValue(value) {}
-
-MediaResource::MediaResource(Type type, SubType subType, uint64_t value)
- : mType(type),
- mSubType(subType),
- mValue(value) {}
-
-MediaResource::MediaResource(Type type, const std::vector<uint8_t> &id, uint64_t value)
- : mType(type),
- mSubType(kUnspecifiedSubType),
- mValue(value),
- mId(id) {}
-
-void MediaResource::readFromParcel(const Parcel &parcel) {
- mType = static_cast<Type>(parcel.readInt32());
- mSubType = static_cast<SubType>(parcel.readInt32());
- mValue = parcel.readUint64();
- parcel.readByteVector(&mId);
+MediaResource::MediaResource(Type type, int64_t value) {
+ this->type = type;
+ this->subType = SubType::kUnspecifiedSubType;
+ this->value = value;
}
-void MediaResource::writeToParcel(Parcel *parcel) const {
- parcel->writeInt32(static_cast<int32_t>(mType));
- parcel->writeInt32(static_cast<int32_t>(mSubType));
- parcel->writeUint64(mValue);
- parcel->writeByteVector(mId);
+MediaResource::MediaResource(Type type, SubType subType, int64_t value) {
+ this->type = type;
+ this->subType = subType;
+ this->value = value;
}
-static String8 bytesToHexString(const std::vector<uint8_t> &bytes) {
+MediaResource::MediaResource(Type type, const std::vector<int8_t> &id, int64_t value) {
+ this->type = type;
+ this->subType = SubType::kUnspecifiedSubType;
+ this->id = id;
+ this->value = value;
+}
+
+//static
+MediaResource MediaResource::CodecResource(bool secure, bool video) {
+ return MediaResource(
+ secure ? Type::kSecureCodec : Type::kNonSecureCodec,
+ video ? SubType::kVideoCodec : SubType::kAudioCodec,
+ 1);
+}
+
+//static
+MediaResource MediaResource::GraphicMemoryResource(int64_t value) {
+ return MediaResource(Type::kGraphicMemory, value);
+}
+
+//static
+MediaResource MediaResource::CpuBoostResource() {
+ return MediaResource(Type::kCpuBoost, 1);
+}
+
+//static
+MediaResource MediaResource::VideoBatteryResource() {
+ return MediaResource(Type::kBattery, SubType::kVideoCodec, 1);
+}
+
+//static
+MediaResource MediaResource::DrmSessionResource(const std::vector<int8_t> &id, int64_t value) {
+ return MediaResource(Type::kDrmSession, id, value);
+}
+
+static String8 bytesToHexString(const std::vector<int8_t> &bytes) {
String8 str;
for (auto &b : bytes) {
str.appendFormat("%02x", b);
@@ -66,24 +78,14 @@
return str;
}
-String8 MediaResource::toString() const {
+String8 toString(const MediaResourceParcel& resource) {
String8 str;
- str.appendFormat("%s/%s:[%s]:%llu",
- asString(mType), asString(mSubType),
- bytesToHexString(mId).c_str(),
- (unsigned long long)mValue);
+
+ str.appendFormat("%s/%s:[%s]:%lld",
+ asString(resource.type), asString(resource.subType),
+ bytesToHexString(resource.id).c_str(),
+ (long long)resource.value);
return str;
}
-bool MediaResource::operator==(const MediaResource &other) const {
- return (other.mType == mType)
- && (other.mSubType == mSubType)
- && (other.mValue == mValue)
- && (other.mId == mId);
-}
-
-bool MediaResource::operator!=(const MediaResource &other) const {
- return !(*this == other);
-}
-
}; // namespace android
diff --git a/media/libmedia/MediaResourcePolicy.cpp b/media/libmedia/MediaResourcePolicy.cpp
index 5210825..afa971d 100644
--- a/media/libmedia/MediaResourcePolicy.cpp
+++ b/media/libmedia/MediaResourcePolicy.cpp
@@ -16,33 +16,32 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaResourcePolicy"
-#include <utils/Log.h>
+
+#include <aidl/android/media/IResourceManagerService.h>
#include <media/MediaResourcePolicy.h>
+#include <utils/Log.h>
namespace android {
-const char kPolicySupportsMultipleSecureCodecs[] = "supports-multiple-secure-codecs";
-const char kPolicySupportsSecureWithNonSecureCodec[] = "supports-secure-with-non-secure-codec";
-
-MediaResourcePolicy::MediaResourcePolicy() {}
-
-MediaResourcePolicy::MediaResourcePolicy(String8 type, String8 value)
- : mType(type),
- mValue(value) {}
-
-void MediaResourcePolicy::readFromParcel(const Parcel &parcel) {
- mType = parcel.readString8();
- mValue = parcel.readString8();
+using aidl::android::media::IResourceManagerService;
+//static
+const char* MediaResourcePolicy::kPolicySupportsMultipleSecureCodecs() {
+ return IResourceManagerService::kPolicySupportsMultipleSecureCodecs;
+}
+//static
+const char* MediaResourcePolicy::kPolicySupportsSecureWithNonSecureCodec() {
+ return IResourceManagerService::kPolicySupportsSecureWithNonSecureCodec;
}
-void MediaResourcePolicy::writeToParcel(Parcel *parcel) const {
- parcel->writeString8(mType);
- parcel->writeString8(mValue);
+MediaResourcePolicy::MediaResourcePolicy(
+ const std::string& type, const std::string& value) {
+ this->type = type;
+ this->value = value;
}
-String8 MediaResourcePolicy::toString() const {
+String8 toString(const MediaResourcePolicyParcel &policy) {
String8 str;
- str.appendFormat("%s:%s", mType.string(), mValue.string());
+ str.appendFormat("%s:%s", policy.type.c_str(), policy.value.c_str());
return str;
}
diff --git a/media/libmedia/aidl/android/IDataSource.aidl b/media/libmedia/aidl/android/IDataSource.aidl
new file mode 100644
index 0000000..fb954bf
--- /dev/null
+++ b/media/libmedia/aidl/android/IDataSource.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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;
+
+/** @hide */
+interface IDataSource {
+ // Stub for manual implementation
+}
diff --git a/media/libmedia/aidl/android/IMediaExtractor.aidl b/media/libmedia/aidl/android/IMediaExtractor.aidl
new file mode 100644
index 0000000..5ba68e6
--- /dev/null
+++ b/media/libmedia/aidl/android/IMediaExtractor.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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;
+
+/** @hide */
+interface IMediaExtractor {
+ // Stub for manual implementation
+}
diff --git a/media/libmedia/aidl/android/IMediaExtractorService.aidl b/media/libmedia/aidl/android/IMediaExtractorService.aidl
new file mode 100644
index 0000000..c57fa16
--- /dev/null
+++ b/media/libmedia/aidl/android/IMediaExtractorService.aidl
@@ -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 android;
+
+import android.IDataSource;
+import android.IMediaExtractor;
+
+/**
+ * Binder interface for the media extractor service
+ *
+ * @hide
+ */
+interface IMediaExtractorService {
+
+ IMediaExtractor makeExtractor(IDataSource source, @nullable @utf8InCpp String mime);
+ IDataSource makeIDataSource(in FileDescriptor fd, long offset, long length);
+ @utf8InCpp String[] getSupportedTypes();
+}
diff --git a/media/libmedia/aidl/android/media/IResourceManagerClient.aidl b/media/libmedia/aidl/android/media/IResourceManagerClient.aidl
new file mode 100644
index 0000000..4c3ef47
--- /dev/null
+++ b/media/libmedia/aidl/android/media/IResourceManagerClient.aidl
@@ -0,0 +1,39 @@
+/**
+ * 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.media;
+
+/**
+ * IResourceManagerClient interface for the ResourceManagerService to
+ * call the client.
+ *
+ * {@hide}
+ */
+interface IResourceManagerClient {
+ /**
+ * Instruct the client to reclaim its resources.
+ *
+ * @return true if the reclaim was successful and false otherwise.
+ */
+ boolean reclaimResource();
+
+ /**
+ * Retrieve the name of the client.
+ *
+ * @return name of the client.
+ */
+ @utf8InCpp String getName();
+}
diff --git a/media/libmedia/aidl/android/media/IResourceManagerService.aidl b/media/libmedia/aidl/android/media/IResourceManagerService.aidl
new file mode 100644
index 0000000..3e6f8db
--- /dev/null
+++ b/media/libmedia/aidl/android/media/IResourceManagerService.aidl
@@ -0,0 +1,87 @@
+/**
+ * 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.media;
+
+import android.media.IResourceManagerClient;
+import android.media.MediaResourceParcel;
+import android.media.MediaResourcePolicyParcel;
+
+/**
+ * ResourceManagerService interface that keeps track of media resource
+ * owned by clients, and reclaims resources based on configured policies
+ * when necessary.
+ *
+ * {@hide}
+ */
+interface IResourceManagerService {
+ const @utf8InCpp String kPolicySupportsMultipleSecureCodecs
+ = "supports-multiple-secure-codecs";
+ const @utf8InCpp String kPolicySupportsSecureWithNonSecureCodec
+ = "supports-secure-with-non-secure-codec";
+
+ /**
+ * Configure the ResourceManagerService to adopted particular policies when
+ * managing the resources.
+ *
+ * @param policies an array of policies to be adopted.
+ */
+ void config(in MediaResourcePolicyParcel[] policies);
+
+ /**
+ * Add a client to a process with a list of resources.
+ *
+ * @param pid pid of the client.
+ * @param uid uid of the client.
+ * @param clientId an identifier that uniquely identifies the client within the pid.
+ * @param client interface for the ResourceManagerService to call the client.
+ * @param resources an array of resources to be added.
+ */
+ void addResource(
+ int pid,
+ int uid,
+ long clientId,
+ IResourceManagerClient client,
+ in MediaResourceParcel[] resources);
+
+ /**
+ * Remove the listed resources from a client.
+ *
+ * @param pid pid from which the list of resources will be removed.
+ * @param clientId clientId within the pid from which the list of resources will be removed.
+ * @param resources an array of resources to be removed from the client.
+ */
+ void removeResource(int pid, long clientId, in MediaResourceParcel[] resources);
+
+ /**
+ * Remove all resources from a client.
+ *
+ * @param pid pid from which the client's resources will be removed.
+ * @param clientId clientId within the pid that will be removed.
+ */
+ void removeClient(int pid, long clientId);
+
+ /**
+ * Tries to reclaim resource from processes with lower priority than the
+ * calling process according to the requested resources.
+ *
+ * @param callingPid pid of the calling process.
+ * @param resources an array of resources to be reclaimed.
+ *
+ * @return true if the reclaim was successful and false otherwise.
+ */
+ boolean reclaimResource(int callingPid, in MediaResourceParcel[] resources);
+}
diff --git a/media/libmedia/aidl/android/media/MediaResourceParcel.aidl b/media/libmedia/aidl/android/media/MediaResourceParcel.aidl
new file mode 100644
index 0000000..b0f2b71
--- /dev/null
+++ b/media/libmedia/aidl/android/media/MediaResourceParcel.aidl
@@ -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.
+ */
+
+package android.media;
+
+import android.media.MediaResourceType;
+import android.media.MediaResourceSubType;
+
+/**
+ * Description of a media resource to be tracked by MediaResourceManager.
+ *
+ * {@hide}
+ */
+parcelable MediaResourceParcel {
+ // TODO: default enum value is not supported yet.
+ // Set default enum value when b/142739329 is fixed.
+
+ /**
+ * Type of the media resource.
+ */
+ MediaResourceType type;// = MediaResourceTypeEnum::kUnspecified;
+
+ /**
+ * Sub-type of the media resource.
+ */
+ MediaResourceSubType subType;// = MediaResourceSubTypeEnum::kUnspecifiedSubType;
+
+ /**
+ * Identifier of the media resource (eg. Drm session id).
+ */
+ byte[] id;
+
+ /**
+ * Number of units of the media resource (bytes of graphic memory, number of codecs, etc.).
+ */
+ long value = 0;
+}
diff --git a/media/libmedia/aidl/android/media/MediaResourcePolicyParcel.aidl b/media/libmedia/aidl/android/media/MediaResourcePolicyParcel.aidl
new file mode 100644
index 0000000..4ea859a
--- /dev/null
+++ b/media/libmedia/aidl/android/media/MediaResourcePolicyParcel.aidl
@@ -0,0 +1,33 @@
+/**
+ * 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.media;
+
+/**
+ * Description of a policy to be adopted by ResourceManagerService.
+ * {@hide}
+ */
+parcelable MediaResourcePolicyParcel {
+ /**
+ * Name of the policy to be adopted.
+ */
+ @utf8InCpp String type;
+
+ /**
+ * Value of the policy to be adopted.
+ */
+ @utf8InCpp String value;
+}
diff --git a/media/libmedia/aidl/android/media/MediaResourceSubType.aidl b/media/libmedia/aidl/android/media/MediaResourceSubType.aidl
new file mode 100644
index 0000000..af2ba68
--- /dev/null
+++ b/media/libmedia/aidl/android/media/MediaResourceSubType.aidl
@@ -0,0 +1,29 @@
+/**
+ * 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.media;
+
+/**
+ * Sub-type enums of media resources.
+ *
+ * {@hide}
+ */
+@Backing(type="int")
+enum MediaResourceSubType {
+ kUnspecifiedSubType = 0,
+ kAudioCodec = 1,
+ kVideoCodec = 2,
+}
diff --git a/media/libmedia/aidl/android/media/MediaResourceType.aidl b/media/libmedia/aidl/android/media/MediaResourceType.aidl
new file mode 100644
index 0000000..b2bb71b
--- /dev/null
+++ b/media/libmedia/aidl/android/media/MediaResourceType.aidl
@@ -0,0 +1,33 @@
+/**
+ * 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.media;
+
+/**
+ * Type enums of media resources.
+ *
+ * {@hide}
+ */
+@Backing(type="int")
+enum MediaResourceType {
+ kUnspecified = 0,
+ kSecureCodec = 1,
+ kNonSecureCodec = 2,
+ kGraphicMemory = 3,
+ kCpuBoost = 4,
+ kBattery = 5,
+ kDrmSession = 6,
+}
diff --git a/media/libmedia/aidl_api/resourcemanager_aidl_interface/.hash b/media/libmedia/aidl_api/resourcemanager_aidl_interface/.hash
new file mode 100644
index 0000000..e56d56b
--- /dev/null
+++ b/media/libmedia/aidl_api/resourcemanager_aidl_interface/.hash
@@ -0,0 +1,18 @@
+///////////////////////////////////////////////////////////////////////////////
+// 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
new file mode 100644
index 0000000..20bfe72
--- /dev/null
+++ b/media/libmedia/aidl_api/resourcemanager_aidl_interface/1/android/media/IResourceManagerClient.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// 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
new file mode 100644
index 0000000..53cf036
--- /dev/null
+++ b/media/libmedia/aidl_api/resourcemanager_aidl_interface/1/android/media/IResourceManagerService.aidl
@@ -0,0 +1,27 @@
+///////////////////////////////////////////////////////////////////////////////
+// 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
new file mode 100644
index 0000000..47ea9bc
--- /dev/null
+++ b/media/libmedia/aidl_api/resourcemanager_aidl_interface/1/android/media/MediaResourceParcel.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// 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
new file mode 100644
index 0000000..85d2588
--- /dev/null
+++ b/media/libmedia/aidl_api/resourcemanager_aidl_interface/1/android/media/MediaResourcePolicyParcel.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// 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
new file mode 100644
index 0000000..19b68af
--- /dev/null
+++ b/media/libmedia/aidl_api/resourcemanager_aidl_interface/1/android/media/MediaResourceSubType.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// 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
new file mode 100644
index 0000000..6a123fc
--- /dev/null
+++ b/media/libmedia/aidl_api/resourcemanager_aidl_interface/1/android/media/MediaResourceType.aidl
@@ -0,0 +1,28 @@
+///////////////////////////////////////////////////////////////////////////////
+// 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/media/IDataSource.h b/media/libmedia/include/android/IDataSource.h
similarity index 100%
rename from media/libmedia/include/media/IDataSource.h
rename to media/libmedia/include/android/IDataSource.h
diff --git a/media/libmedia/include/media/IMediaExtractor.h b/media/libmedia/include/android/IMediaExtractor.h
similarity index 100%
rename from media/libmedia/include/media/IMediaExtractor.h
rename to media/libmedia/include/android/IMediaExtractor.h
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/IMediaExtractorService.h b/media/libmedia/include/media/IMediaExtractorService.h
deleted file mode 100644
index 5ce2cdb..0000000
--- a/media/libmedia/include/media/IMediaExtractorService.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2013 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_IMEDIAEXTRACTORSERVICE_H
-#define ANDROID_IMEDIAEXTRACTORSERVICE_H
-
-#include <unordered_set>
-
-#include <binder/IInterface.h>
-#include <binder/IMemory.h>
-#include <binder/Parcel.h>
-#include <media/IDataSource.h>
-#include <media/IMediaExtractor.h>
-
-namespace android {
-
-class IMediaExtractorService: public IInterface
-{
-public:
- DECLARE_META_INTERFACE(MediaExtractorService);
-
- virtual sp<IMediaExtractor> makeExtractor(const sp<IDataSource> &source, const char *mime) = 0;
-
- virtual sp<IDataSource> makeIDataSource(int fd, int64_t offset, int64_t length) = 0;
-
- virtual std::unordered_set<std::string> getSupportedTypes() = 0;
-};
-
-class BnMediaExtractorService: public BnInterface<IMediaExtractorService>
-{
-public:
- virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags = 0);
-};
-
-} // namespace android
-
-#endif // ANDROID_IMEDIAEXTRACTORSERVICE_H
diff --git a/media/libmedia/include/media/IMediaMetadataRetriever.h b/media/libmedia/include/media/IMediaMetadataRetriever.h
index c6f422d..28d2192 100644
--- a/media/libmedia/include/media/IMediaMetadataRetriever.h
+++ b/media/libmedia/include/media/IMediaMetadataRetriever.h
@@ -48,9 +48,8 @@
int index, int colorFormat, bool metaOnly, bool thumbnail) = 0;
virtual sp<IMemory> getImageRectAtIndex(
int index, int colorFormat, int left, int top, int right, int bottom) = 0;
- virtual status_t getFrameAtIndex(
- std::vector<sp<IMemory> > *frames,
- int frameIndex, int numFrames, int colorFormat, bool metaOnly) = 0;
+ virtual sp<IMemory> getFrameAtIndex(
+ int index, int colorFormat, bool metaOnly) = 0;
virtual sp<IMemory> extractAlbumArt() = 0;
virtual const char* extractMetadata(int keyCode) = 0;
};
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/IMediaRecorder.h b/media/libmedia/include/media/IMediaRecorder.h
index f9c557c..651bd5e 100644
--- a/media/libmedia/include/media/IMediaRecorder.h
+++ b/media/libmedia/include/media/IMediaRecorder.h
@@ -44,6 +44,8 @@
virtual status_t setPreviewSurface(const sp<IGraphicBufferProducer>& surface) = 0;
virtual status_t setVideoSource(int vs) = 0;
virtual status_t setAudioSource(int as) = 0;
+ virtual status_t setPrivacySensitive(bool privacySensitive) = 0;
+ virtual status_t isPrivacySensitive(bool *privacySensitive) const = 0;
virtual status_t setOutputFormat(int of) = 0;
virtual status_t setVideoEncoder(int ve) = 0;
virtual status_t setAudioEncoder(int ae) = 0;
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/IOMX.h b/media/libmedia/include/media/IOMX.h
index 7e7c2d2..70c8a74 100644
--- a/media/libmedia/include/media/IOMX.h
+++ b/media/libmedia/include/media/IOMX.h
@@ -34,9 +34,17 @@
#include <media/openmax/OMX_VideoExt.h>
namespace android {
+namespace hardware {
+namespace media {
+namespace omx {
+namespace V1_0 {
+struct IGraphicBufferSource;
+} // namespace V1_0
+} // namespace omx
+} // namespace media
+} // namespace hardware
class IGraphicBufferProducer;
-class IGraphicBufferSource;
class IMemory;
class IOMXBufferSource;
class IOMXNode;
@@ -82,7 +90,7 @@
virtual status_t createInputSurface(
sp<IGraphicBufferProducer> *bufferProducer,
- sp<IGraphicBufferSource> *bufferSource) = 0;
+ sp<hardware::media::omx::V1_0::IGraphicBufferSource> *bufferSource) = 0;
};
class IOMXNode : public IInterface {
diff --git a/media/libmedia/include/media/IResourceManagerClient.h b/media/libmedia/include/media/IResourceManagerClient.h
deleted file mode 100644
index aa0cd88..0000000
--- a/media/libmedia/include/media/IResourceManagerClient.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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 ANDROID_IRESOURCEMANAGERCLIENT_H
-#define ANDROID_IRESOURCEMANAGERCLIENT_H
-
-#include <utils/RefBase.h>
-#include <utils/String8.h>
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-
-namespace android {
-
-class IResourceManagerClient: public IInterface
-{
-public:
- DECLARE_META_INTERFACE(ResourceManagerClient);
-
- virtual bool reclaimResource() = 0;
- virtual String8 getName() = 0;
-};
-
-// ----------------------------------------------------------------------------
-
-class BnResourceManagerClient: public BnInterface<IResourceManagerClient>
-{
-public:
- virtual status_t onTransact(uint32_t code,
- const Parcel &data,
- Parcel *reply,
- uint32_t flags = 0);
-};
-
-}; // namespace android
-
-#endif // ANDROID_IRESOURCEMANAGERCLIENT_H
diff --git a/media/libmedia/include/media/IResourceManagerService.h b/media/libmedia/include/media/IResourceManagerService.h
deleted file mode 100644
index 8992f8b..0000000
--- a/media/libmedia/include/media/IResourceManagerService.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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 ANDROID_IRESOURCEMANAGERSERVICE_H
-#define ANDROID_IRESOURCEMANAGERSERVICE_H
-
-#include <utils/Errors.h> // for status_t
-#include <utils/KeyedVector.h>
-#include <utils/RefBase.h>
-#include <utils/String8.h>
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-
-#include <media/IResourceManagerClient.h>
-#include <media/MediaResource.h>
-#include <media/MediaResourcePolicy.h>
-
-namespace android {
-
-class IResourceManagerService: public IInterface
-{
-public:
- DECLARE_META_INTERFACE(ResourceManagerService);
-
- virtual void config(const Vector<MediaResourcePolicy> &policies) = 0;
-
- virtual void addResource(
- int pid,
- int uid,
- int64_t clientId,
- const sp<IResourceManagerClient> client,
- const Vector<MediaResource> &resources) = 0;
-
- virtual void removeResource(int pid, int64_t clientId,
- const Vector<MediaResource> &resources) = 0;
-
- virtual void removeClient(int pid, int64_t clientId) = 0;
-
- virtual bool reclaimResource(
- int callingPid,
- const Vector<MediaResource> &resources) = 0;
-};
-
-// ----------------------------------------------------------------------------
-
-class BnResourceManagerService: public BnInterface<IResourceManagerService>
-{
-public:
- virtual status_t onTransact(uint32_t code,
- const Parcel &data,
- Parcel *reply,
- uint32_t flags = 0);
-};
-
-}; // namespace android
-
-#endif // ANDROID_IRESOURCEMANAGERSERVICE_H
diff --git a/media/libmedia/include/media/LinearMap.h b/media/libmedia/include/media/LinearMap.h
deleted file mode 100644
index 2220a0c..0000000
--- a/media/libmedia/include/media/LinearMap.h
+++ /dev/null
@@ -1,366 +0,0 @@
-/*
- * 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 ANDROID_LINEAR_MAP_H
-#define ANDROID_LINEAR_MAP_H
-
-#include <stdint.h>
-
-namespace android {
-
-/*
-A general purpose lookup utility that defines a mapping between X and Y as a
-continuous set of line segments with shared (x, y) end-points.
-The (x, y) points must be added in order, monotonically increasing in both x and y;
-a log warning is emitted if this does not happen (See general usage notes below).
-
-A limited history of (x, y) points is kept for space reasons (See general usage notes).
-
-In AudioFlinger, we use the LinearMap to associate track frames to
-sink frames. When we want to obtain a client track timestamp, we first
-get a timestamp from the sink. The sink timestamp's position (mPosition)
-corresponds to the sink frames written. We use LinearMap to figure out which track frame
-the sink frame corresponds to. This allows us to substitute a track frame for the
-the sink frame (keeping the mTime identical) and return that timestamp back to the client.
-
-The method findX() can be used to retrieve an x value from a given y value and is
-used for timestamps, similarly for findY() which is provided for completeness.
-
-We update the (track frame, sink frame) points in the LinearMap each time we write data
-to the sink by the AudioFlinger PlaybackThread (MixerThread).
-
-
-AudioFlinger Timestamp Notes:
-
-1) Example: Obtaining a track timestamp during playback. In this case, the LinearMap
-looks something like this:
-
-Track Frame Sink Frame
-(track start)
-0 50000 (track starts here, the sink may already be running)
-1000 51000
-2000 52000
-
-When we request a track timestamp, we call the sink getTimestamp() and get for example
-mPosition = 51020. Using the LinearMap, we find we have played to track frame 1020.
-We substitute the sink mPosition of 51020 with the track position 1020,
-and return that timestamp to the app.
-
-2) Example: Obtaining a track timestamp duing pause. In this case, the LinearMap
-looks something like this:
-
-Track Frame Sink Frame
-... (some time has gone by)
-15000 30000
-16000 31000
-17000 32000
-(pause here)
-(suppose we call sink getTimestamp() here and get sink mPosition = 31100; that means
- we have played to track frame 16100. The track timestamp mPosition will
- continue to advance until the sink timestamp returns a value of mPosition
- greater than 32000, corresponding to track frame 17000 when the pause was called).
-17000 33000
-17000 34000
-...
-
-3) If the track underruns, it appears as if a pause was called on that track.
-
-4) If there is an underrun in the HAL layer, then it may be possible that
-the sink getTimestamp() will return a value greater than the number of frames written
-(it should always be less). This should be rare, if not impossible by some
-HAL implementations of the sink getTimestamp. In that case, timing is lost
-and we will return the most recent track frame written.
-
-5) When called with no points in the map, findX() returns the start value (default 0).
-This is consistent with starting after a stop() or flush().
-
-6) Resuming after Track standby will be similar to coming out of pause, as the HAL ensures
-framesWritten() and getTimestamp() are contiguous for non-offloaded/direct tracks.
-
-7) LinearMap works for different speeds and sample rates as it uses
-linear interpolation. Since AudioFlinger only updates speed and sample rate
-exactly at the sample points pushed into the LinearMap, the returned values
-from findX() and findY() are accurate regardless of how many speed or sample
-rate changes are made, so long as the coordinate looked up is within the
-sample history.
-
-General usage notes:
-
-1) In order for the LinearMap to work reliably, you cannot look backwards more
-than the size of its circular buffer history, set upon creation (typically 16).
-If you look back further, the position is extrapolated either from a passed in
-extrapolation parameter or from the oldest line segment.
-
-2) Points must monotonically increase in x and y. The increment between adjacent
-points cannot be greater than signed 32 bits. Wrap in the x, y coordinates are supported,
-since we use differences in our computation.
-
-3) If the frame data is discontinuous (due to stop or flush) call reset() to clear
-the sample counter.
-
-4) If (x, y) are not strictly monotonic increasing, i.e. (x2 > x1) and (y2 > y1),
-then one or both of the inverses y = f(x) or x = g(y) may have multiple solutions.
-In that case, the most recent solution is returned by findX() or findY(). We
-do not warn if (x2 == x1) or (y2 == y1), but we do logcat warn if (x2 < x1) or
-(y2 < y1).
-
-5) Due to rounding it is possible x != findX(findY(x)) or y != findY(findX(y))
-even when the inverse exists. Nevertheless, the values should be close.
-
-*/
-
-template <typename T>
-class LinearMap {
-public:
- // This enumeration describes the reliability of the findX() or findY() estimation
- // in descending order.
- enum FindMethod {
- FIND_METHOD_INTERPOLATION, // High reliability (errors due to rounding)
- FIND_METHOD_FORWARD_EXTRAPOLATION, // Reliability based on no future speed changes
- FIND_METHOD_BACKWARD_EXTRAPOLATION, // Reliability based on prior estimated speed
- FIND_METHOD_START_VALUE, // No samples in history, using start value
- };
-
- explicit LinearMap(size_t size)
- : mSize(size),
- mPos(0), // a circular buffer, so could start anywhere. the first sample is at 1.
- mSamples(0),
- // mStepValid(false), // only valid if mSamples > 1
- // mExtrapolateTail(false), // only valid if mSamples > 0
- mX(new T[size]),
- mY(new T[size]) { }
-
- ~LinearMap() {
- delete[] mX;
- delete[] mY;
- }
-
- // Add a new sample point to the linear map.
- //
- // The difference between the new sample and the previous sample
- // in the x or y coordinate must be less than INT32_MAX for purposes
- // of the linear interpolation or extrapolation.
- //
- // The value should be monotonic increasing (e.g. diff >= 0);
- // logcat warnings are issued if they are not.
- __attribute__((no_sanitize("integer")))
- void push(T x, T y) {
- // Assumption: we assume x, y are monotonic increasing values,
- // which (can) wrap in precision no less than 32 bits and have
- // "step" or differences between adjacent points less than 32 bits.
-
- if (mSamples > 0) {
- const bool lastStepValid = mStepValid;
- int32_t xdiff;
- int32_t ydiff;
- // check difference assumption here
- mStepValid = checkedDiff(&xdiff, x, mX[mPos], "x")
- & /* bitwise AND to always warn for ydiff, though logical AND is also OK */
- checkedDiff(&ydiff, y, mY[mPos], "y");
-
- // Optimization: do not add a new sample if the line segment would
- // simply extend the previous line segment. This extends the useful
- // history by removing redundant points.
- if (mSamples > 1 && mStepValid && lastStepValid) {
- const size_t prev = previousPosition();
- const int32_t xdiff2 = x - mX[prev];
- const int32_t ydiff2 = y - mY[prev];
-
- // if both current step and previous step are valid (non-negative and
- // less than INT32_MAX for precision greater than 4 bytes)
- // then the sum of the two steps is valid when the
- // int32_t difference is non-negative.
- if (xdiff2 >= 0 && ydiff2 >= 0
- && (int64_t)xdiff2 * ydiff == (int64_t)ydiff2 * xdiff) {
- // ALOGD("reusing sample! (%u, %u) sample depth %zd", x, y, mSamples);
- mX[mPos] = x;
- mY[mPos] = y;
- return;
- }
- }
- }
- if (++mPos >= mSize) {
- mPos = 0;
- }
- if (mSamples < mSize) {
- mExtrapolateTail = false;
- ++mSamples;
- } else {
- // we enable extrapolation beyond the oldest sample
- // if the sample buffers are completely full and we
- // no longer know the full history.
- mExtrapolateTail = true;
- }
- mX[mPos] = x;
- mY[mPos] = y;
- }
-
- // clear all samples from the circular array
- void reset() {
- // no need to reset mPos, we use a circular buffer.
- // computed values such as mStepValid are set after a subsequent push().
- mSamples = 0;
- }
-
- // returns true if LinearMap contains at least one sample.
- bool hasData() const {
- return mSamples != 0;
- }
-
- // find the corresponding X point from a Y point.
- // See findU for details.
- __attribute__((no_sanitize("integer")))
- T findX(T y, FindMethod *method = NULL, double extrapolation = 0.0, T startValue = 0) const {
- return findU(y, mX, mY, method, extrapolation, startValue);
- }
-
- // find the corresponding Y point from a X point.
- // See findU for details.
- __attribute__((no_sanitize("integer")))
- T findY(T x, FindMethod *method = NULL, double extrapolation = 0.0, T startValue = 0) const {
- return findU(x, mY, mX, method, extrapolation, startValue);
- }
-
-protected:
-
- // returns false if the diff is out of int32_t bounds or negative.
- __attribute__((no_sanitize("integer")))
- static inline bool checkedDiff(int32_t *diff, T x2, T x1, const char *coord) {
- if (sizeof(T) >= 8) {
- const int64_t diff64 = x2 - x1;
- *diff = (int32_t)diff64; // intentionally lose precision
- if (diff64 > INT32_MAX) {
- ALOGW("LinearMap: %s overflow diff(%lld) from %llu - %llu exceeds INT32_MAX",
- coord, (long long)diff64,
- (unsigned long long)x2, (unsigned long long)x1);
- return false;
- } else if (diff64 < 0) {
- ALOGW("LinearMap: %s negative diff(%lld) from %llu - %llu",
- coord, (long long)diff64,
- (unsigned long long)x2, (unsigned long long)x1);
- return false;
- }
- return true;
- }
- // for 32 bit integers we cannot detect overflow (it
- // shows up as a negative difference).
- *diff = x2 - x1;
- if (*diff < 0) {
- ALOGW("LinearMap: %s negative diff(%d) from %u - %u",
- coord, *diff, (unsigned)x2, (unsigned)x1);
- return false;
- }
- return true;
- }
-
- // Returns the previous position in the mSamples array
- // going backwards back steps.
- //
- // Parameters:
- // back: number of backward steps, cannot be less than zero or greater than mSamples.
- //
- __attribute__((no_sanitize("integer")))
- size_t previousPosition(ssize_t back = 1) const {
- LOG_ALWAYS_FATAL_IF(back < 0 || (size_t)back > mSamples, "Invalid back(%zd)", back);
- ssize_t position = mPos - back;
- if (position < 0) position += mSize;
- return (size_t)position;
- }
-
- // A generic implementation of finding the "other coordinate" with coordinates
- // (u, v) = (x, y) or (u, v) = (y, x).
- //
- // Parameters:
- // uArray: the u axis samples.
- // vArray: the v axis samples.
- // method: [out] how the returned value was computed.
- // extrapolation: the slope used when extrapolating from the
- // first sample value or the last sample value in the history.
- // If mExtrapolateTail is set, the slope of the last line segment
- // is used if the extrapolation parameter is zero to continue the tail of history.
- // At this time, we do not use a different value for forward extrapolation from the
- // head of history from backward extrapolation from the tail of history.
- // TODO: back extrapolation value could be stored along with mX, mY in history.
- // startValue: used only when there are no samples in history. One can detect
- // whether there are samples in history by the method hasData().
- //
- __attribute__((no_sanitize("integer")))
- T findU(T v, T *uArray, T *vArray, FindMethod *method,
- double extrapolation, T startValue) const {
- if (mSamples == 0) {
- if (method != NULL) {
- *method = FIND_METHOD_START_VALUE;
- }
- return startValue; // nothing yet
- }
- ssize_t previous = 0;
- int32_t diff = 0;
- for (ssize_t i = 0; i < (ssize_t)mSamples; ++i) {
- size_t current = previousPosition(i);
-
- // Assumption: even though the type "T" may have precision greater
- // than 32 bits, the difference between adjacent points is limited to 32 bits.
- diff = v - vArray[current];
- if (diff >= 0 ||
- (i == (ssize_t)mSamples - 1 && mExtrapolateTail && extrapolation == 0.0)) {
- // ALOGD("depth = %zd out of %zd", i, limit);
- if (i == 0) {
- if (method != NULL) {
- *method = FIND_METHOD_FORWARD_EXTRAPOLATION;
- }
- return uArray[current] + diff * extrapolation;
- }
- // interpolate / extrapolate: For this computation, we
- // must use differentials here otherwise we have inconsistent
- // values on modulo wrap. previous is always valid here since
- // i > 0. we also perform rounding with the assumption
- // that uStep, vStep, and diff are non-negative.
- int32_t uStep = uArray[previous] - uArray[current]; // non-negative
- int32_t vStep = vArray[previous] - vArray[current]; // positive
- T u = uStep <= 0 || vStep <= 0 ? // we do not permit negative ustep or vstep
- uArray[current]
- : ((int64_t)diff * uStep + (vStep >> 1)) / vStep + uArray[current];
- // ALOGD("u:%u diff:%d uStep:%d vStep:%d u_current:%d",
- // u, diff, uStep, vStep, uArray[current]);
- if (method != NULL) {
- *method = (diff >= 0) ?
- FIND_METHOD_INTERPOLATION : FIND_METHOD_BACKWARD_EXTRAPOLATION;
- }
- return u;
- }
- previous = current;
- }
- // previous is always valid here.
- if (method != NULL) {
- *method = FIND_METHOD_BACKWARD_EXTRAPOLATION;
- }
- return uArray[previous] + diff * extrapolation;
- }
-
-private:
- const size_t mSize; // Size of mX and mY arrays (history).
- size_t mPos; // Index in mX and mY of last pushed data;
- // (incremented after push) [0, mSize - 1].
- size_t mSamples; // Number of valid samples in the array [0, mSize].
- bool mStepValid; // Last sample step was valid (non-negative)
- bool mExtrapolateTail; // extrapolate tail using oldest line segment
- T * const mX; // History of X values as a circular array.
- T * const mY; // History of Y values as a circular array.
-};
-
-} // namespace android
-
-#endif // ANDROID_LINEAR_MAP_H
diff --git a/media/libmedia/include/media/MediaMetadataRetrieverInterface.h b/media/libmedia/include/media/MediaMetadataRetrieverInterface.h
index 98d300f..37dc401 100644
--- a/media/libmedia/include/media/MediaMetadataRetrieverInterface.h
+++ b/media/libmedia/include/media/MediaMetadataRetrieverInterface.h
@@ -49,9 +49,8 @@
int index, int colorFormat, bool metaOnly, bool thumbnail) = 0;
virtual sp<IMemory> getImageRectAtIndex(
int index, int colorFormat, int left, int top, int right, int bottom) = 0;
- virtual status_t getFrameAtIndex(
- std::vector<sp<IMemory> >* frames,
- int frameIndex, int numFrames, int colorFormat, bool metaOnly) = 0;
+ virtual sp<IMemory> getFrameAtIndex(
+ int frameIndex, int colorFormat, bool metaOnly) = 0;
virtual MediaAlbumArt* extractAlbumArt() = 0;
virtual const char* extractMetadata(int keyCode) = 0;
};
diff --git a/media/libmedia/include/media/MediaRecorderBase.h b/media/libmedia/include/media/MediaRecorderBase.h
index a2dff31..8493f64 100644
--- a/media/libmedia/include/media/MediaRecorderBase.h
+++ b/media/libmedia/include/media/MediaRecorderBase.h
@@ -39,6 +39,8 @@
virtual status_t init() = 0;
virtual status_t setAudioSource(audio_source_t as) = 0;
+ virtual status_t setPrivacySensitive(bool privacySensitive) = 0 ;
+ virtual status_t isPrivacySensitive(bool *privacySensitive) const = 0;
virtual status_t setVideoSource(video_source vs) = 0;
virtual status_t setOutputFormat(output_format of) = 0;
virtual status_t setAudioEncoder(audio_encoder ae) = 0;
@@ -79,6 +81,7 @@
protected:
+
String16 mOpPackageName;
private:
diff --git a/media/libmedia/include/media/MediaResource.h b/media/libmedia/include/media/MediaResource.h
index e9684f0..e7362c1 100644
--- a/media/libmedia/include/media/MediaResource.h
+++ b/media/libmedia/include/media/MediaResource.h
@@ -18,72 +18,56 @@
#ifndef ANDROID_MEDIA_RESOURCE_H
#define ANDROID_MEDIA_RESOURCE_H
-#include <binder/Parcel.h>
+#include <aidl/android/media/MediaResourceParcel.h>
#include <utils/String8.h>
-#include <vector>
namespace android {
-class MediaResource {
+using aidl::android::media::MediaResourceParcel;
+using aidl::android::media::MediaResourceSubType;
+using aidl::android::media::MediaResourceType;
+
+class MediaResource : public MediaResourceParcel {
public:
- enum Type {
- kUnspecified = 0,
- kSecureCodec,
- kNonSecureCodec,
- kGraphicMemory,
- kCpuBoost,
- kBattery,
- kDrmSession,
- };
+ using Type = MediaResourceType;
+ using SubType = MediaResourceSubType;
- enum SubType {
- kUnspecifiedSubType = 0,
- kAudioCodec,
- kVideoCodec,
- };
+ MediaResource() = delete;
+ MediaResource(Type type, int64_t value);
+ MediaResource(Type type, SubType subType, int64_t value);
+ MediaResource(Type type, const std::vector<int8_t> &id, int64_t value);
- MediaResource();
- MediaResource(Type type, uint64_t value);
- MediaResource(Type type, SubType subType, uint64_t value);
- MediaResource(Type type, const std::vector<uint8_t> &id, uint64_t value);
-
- void readFromParcel(const Parcel &parcel);
- void writeToParcel(Parcel *parcel) const;
-
- String8 toString() const;
-
- bool operator==(const MediaResource &other) const;
- bool operator!=(const MediaResource &other) const;
-
- Type mType;
- SubType mSubType;
- uint64_t mValue;
- // for kDrmSession-type mId is the unique session id obtained via MediaDrm#openSession
- std::vector<uint8_t> mId;
+ static MediaResource CodecResource(bool secure, bool video);
+ static MediaResource GraphicMemoryResource(int64_t value);
+ static MediaResource CpuBoostResource();
+ static MediaResource VideoBatteryResource();
+ static MediaResource DrmSessionResource(const std::vector<int8_t> &id, int64_t value);
};
inline static const char *asString(MediaResource::Type i, const char *def = "??") {
switch (i) {
- case MediaResource::kUnspecified: return "unspecified";
- case MediaResource::kSecureCodec: return "secure-codec";
- case MediaResource::kNonSecureCodec: return "non-secure-codec";
- case MediaResource::kGraphicMemory: return "graphic-memory";
- case MediaResource::kCpuBoost: return "cpu-boost";
- case MediaResource::kBattery: return "battery";
- case MediaResource::kDrmSession: return "drm-session";
- default: return def;
+ case MediaResource::Type::kUnspecified: return "unspecified";
+ case MediaResource::Type::kSecureCodec: return "secure-codec";
+ case MediaResource::Type::kNonSecureCodec: return "non-secure-codec";
+ case MediaResource::Type::kGraphicMemory: return "graphic-memory";
+ case MediaResource::Type::kCpuBoost: return "cpu-boost";
+ case MediaResource::Type::kBattery: return "battery";
+ case MediaResource::Type::kDrmSession: return "drm-session";
+ default: return def;
}
}
inline static const char *asString(MediaResource::SubType i, const char *def = "??") {
switch (i) {
- case MediaResource::kUnspecifiedSubType: return "unspecified";
- case MediaResource::kAudioCodec: return "audio-codec";
- case MediaResource::kVideoCodec: return "video-codec";
+ case MediaResource::SubType::kUnspecifiedSubType: return "unspecified";
+ case MediaResource::SubType::kAudioCodec: return "audio-codec";
+ case MediaResource::SubType::kVideoCodec: return "video-codec";
default: return def;
}
}
+String8 toString(const MediaResourceParcel& resource);
+
}; // namespace android
#endif // ANDROID_MEDIA_RESOURCE_H
diff --git a/media/libmedia/include/media/MediaResourcePolicy.h b/media/libmedia/include/media/MediaResourcePolicy.h
index 9bc2eec..052395b 100644
--- a/media/libmedia/include/media/MediaResourcePolicy.h
+++ b/media/libmedia/include/media/MediaResourcePolicy.h
@@ -18,28 +18,24 @@
#ifndef ANDROID_MEDIA_RESOURCE_POLICY_H
#define ANDROID_MEDIA_RESOURCE_POLICY_H
-#include <binder/Parcel.h>
+#include <aidl/android/media/MediaResourcePolicyParcel.h>
#include <utils/String8.h>
namespace android {
-extern const char kPolicySupportsMultipleSecureCodecs[];
-extern const char kPolicySupportsSecureWithNonSecureCodec[];
+using aidl::android::media::MediaResourcePolicyParcel;
-class MediaResourcePolicy {
+class MediaResourcePolicy : public MediaResourcePolicyParcel {
public:
- MediaResourcePolicy();
- MediaResourcePolicy(String8 type, String8 value);
+ MediaResourcePolicy() = delete;
+ MediaResourcePolicy(const std::string& type, const std::string& value);
- void readFromParcel(const Parcel &parcel);
- void writeToParcel(Parcel *parcel) const;
-
- String8 toString() const;
-
- String8 mType;
- String8 mValue;
+ static const char* kPolicySupportsMultipleSecureCodecs();
+ static const char* kPolicySupportsSecureWithNonSecureCodec();
};
+String8 toString(const MediaResourcePolicyParcel &policy);
+
}; // namespace android
#endif // ANDROID_MEDIA_RESOURCE_POLICY_H
diff --git a/media/libmedia/include/media/MidiIoWrapper.h b/media/libmedia/include/media/MidiIoWrapper.h
index b19d49e..7beb619 100644
--- a/media/libmedia/include/media/MidiIoWrapper.h
+++ b/media/libmedia/include/media/MidiIoWrapper.h
@@ -19,8 +19,6 @@
#include <libsonivox/eas_types.h>
-#include <media/DataSourceBase.h>
-
namespace android {
struct CDataSource;
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/include/media/PluginMetricsReporting.h b/media/libmedia/include/media/PluginMetricsReporting.h
index e00bd43..f71c52d 100644
--- a/media/libmedia/include/media/PluginMetricsReporting.h
+++ b/media/libmedia/include/media/PluginMetricsReporting.h
@@ -18,6 +18,7 @@
#define PLUGIN_METRICS_REPORTING_H_
+#include <sys/types.h>
#include <utils/Errors.h>
#include <utils/String8.h>
@@ -26,7 +27,7 @@
status_t reportDrmPluginMetrics(const std::string& b64EncodedMetrics,
const String8& vendorName,
const String8& description,
- const String8& appPackageName);
+ uid_t appUid);
} // namespace android
diff --git a/media/libmedia/include/media/mediametadataretriever.h b/media/libmedia/include/media/mediametadataretriever.h
index d29e97d..138a014 100644
--- a/media/libmedia/include/media/mediametadataretriever.h
+++ b/media/libmedia/include/media/mediametadataretriever.h
@@ -98,9 +98,8 @@
int colorFormat = HAL_PIXEL_FORMAT_RGB_565, bool metaOnly = false, bool thumbnail = false);
sp<IMemory> getImageRectAtIndex(
int index, int colorFormat, int left, int top, int right, int bottom);
- status_t getFrameAtIndex(
- std::vector<sp<IMemory> > *frames, int frameIndex, int numFrames = 1,
- int colorFormat = HAL_PIXEL_FORMAT_RGB_565, bool metaOnly = false);
+ sp<IMemory> getFrameAtIndex(
+ int index, int colorFormat = HAL_PIXEL_FORMAT_RGB_565, bool metaOnly = false);
sp<IMemory> extractAlbumArt();
const char* extractMetadata(int keyCode);
diff --git a/media/libmedia/include/media/mediarecorder.h b/media/libmedia/include/media/mediarecorder.h
index 2dd4b7f..6e2d94d 100644
--- a/media/libmedia/include/media/mediarecorder.h
+++ b/media/libmedia/include/media/mediarecorder.h
@@ -236,6 +236,8 @@
status_t setPreviewSurface(const sp<IGraphicBufferProducer>& surface);
status_t setVideoSource(int vs);
status_t setAudioSource(int as);
+ status_t setPrivacySensitive(bool privacySensitive);
+ status_t isPrivacySensitive(bool *privacySensitive) const;
status_t setOutputFormat(int of);
status_t setVideoEncoder(int ve);
status_t setAudioEncoder(int ae);
diff --git a/media/libmedia/include/media/omx/1.0/Conversion.h b/media/libmedia/include/media/omx/1.0/Conversion.h
index 6dc46b7..811936b 100644
--- a/media/libmedia/include/media/omx/1.0/Conversion.h
+++ b/media/libmedia/include/media/omx/1.0/Conversion.h
@@ -45,7 +45,6 @@
#include <android/hardware/media/omx/1.0/IOmxObserver.h>
#include <android/hardware/media/omx/1.0/IGraphicBufferSource.h>
-#include <android/IGraphicBufferSource.h>
#include <android/IOMXBufferSource.h>
namespace android {
diff --git a/media/libmedia/include/media/omx/1.0/WOmx.h b/media/libmedia/include/media/omx/1.0/WOmx.h
index 0680eec..46ada9b 100644
--- a/media/libmedia/include/media/omx/1.0/WOmx.h
+++ b/media/libmedia/include/media/omx/1.0/WOmx.h
@@ -67,7 +67,7 @@
sp<IOMXNode>* omxNode) override;
status_t createInputSurface(
sp<::android::IGraphicBufferProducer>* bufferProducer,
- sp<::android::IGraphicBufferSource>* bufferSource) override;
+ sp<::android::hardware::media::omx::V1_0::IGraphicBufferSource>* bufferSource) override;
};
} // namespace utils
diff --git a/media/libmedia/mediametadataretriever.cpp b/media/libmedia/mediametadataretriever.cpp
index e61b04d..2ae76b3 100644
--- a/media/libmedia/mediametadataretriever.cpp
+++ b/media/libmedia/mediametadataretriever.cpp
@@ -179,18 +179,16 @@
index, colorFormat, left, top, right, bottom);
}
-status_t MediaMetadataRetriever::getFrameAtIndex(
- std::vector<sp<IMemory> > *frames,
- int frameIndex, int numFrames, int colorFormat, bool metaOnly) {
- ALOGV("getFrameAtIndex: frameIndex(%d), numFrames(%d), colorFormat(%d) metaOnly(%d)",
- frameIndex, numFrames, colorFormat, metaOnly);
+sp<IMemory> MediaMetadataRetriever::getFrameAtIndex(
+ int index, int colorFormat, bool metaOnly) {
+ ALOGV("getFrameAtIndex: index(%d), colorFormat(%d) metaOnly(%d)",
+ index, colorFormat, metaOnly);
Mutex::Autolock _l(mLock);
if (mRetriever == 0) {
ALOGE("retriever is not initialized");
- return INVALID_OPERATION;
+ return NULL;
}
- return mRetriever->getFrameAtIndex(
- frames, frameIndex, numFrames, colorFormat, metaOnly);
+ return mRetriever->getFrameAtIndex(index, colorFormat, metaOnly);
}
const char* MediaMetadataRetriever::extractMetadata(int keyCode)
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index 26908e5..60d2f85 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -17,6 +17,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaPlayerNative"
+#include <utils/Log.h>
#include <fcntl.h>
#include <inttypes.h>
@@ -24,25 +25,15 @@
#include <sys/types.h>
#include <unistd.h>
-#include <utils/Log.h>
-#include <binder/IServiceManager.h>
+#include <android/IDataSource.h>
#include <binder/IPCThreadState.h>
-
-#include <gui/Surface.h>
-
#include <media/mediaplayer.h>
#include <media/AudioResamplerPublic.h>
#include <media/AudioSystem.h>
#include <media/AVSyncSettings.h>
-#include <media/IDataSource.h>
-#include <media/MediaAnalyticsItem.h>
-
-#include <binder/MemoryBase.h>
-
#include <utils/KeyedVector.h>
#include <utils/String8.h>
-
#include <system/audio.h>
#include <system/window.h>
diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp
index 4570af9..70655d5 100644
--- a/media/libmedia/mediarecorder.cpp
+++ b/media/libmedia/mediarecorder.cpp
@@ -178,6 +178,47 @@
return ret;
}
+status_t MediaRecorder::setPrivacySensitive(bool privacySensitive)
+{
+ ALOGV("%s(%s)", __func__, privacySensitive ? "true" : "false");
+ if (mMediaRecorder == NULL) {
+ ALOGE("%s: media recorder is not initialized yet", __func__);
+ return INVALID_OPERATION;
+ }
+
+ if (!(mCurrentState & MEDIA_RECORDER_INITIALIZED) || !mIsAudioSourceSet) {
+ ALOGE("%s called in an invalid state(%d) or audio source not (%d)",
+ __func__, mCurrentState, mIsAudioSourceSet);
+ return INVALID_OPERATION;
+ }
+
+ status_t ret = mMediaRecorder->setPrivacySensitive(privacySensitive);
+ if (OK != ret) {
+ ALOGV("%s failed: %d", __func__, ret);
+ mCurrentState = MEDIA_RECORDER_ERROR;
+ return ret;
+ }
+ return ret;
+}
+
+status_t MediaRecorder::isPrivacySensitive(bool *privacySensitive) const
+{
+ if (mMediaRecorder == NULL) {
+ ALOGE("%s: media recorder is not initialized yet", __func__);
+ return INVALID_OPERATION;
+ }
+
+ if (!(mCurrentState & MEDIA_RECORDER_INITIALIZED) || !mIsAudioSourceSet) {
+ ALOGE("%s called in an invalid state(%d) or audio source not (%d)",
+ __func__, mCurrentState, mIsAudioSourceSet);
+ return INVALID_OPERATION;
+ }
+
+ status_t ret = mMediaRecorder->isPrivacySensitive(privacySensitive);
+ ALOGV("%s status: %d eanbled %s", __func__, ret, *privacySensitive ? "enabled" : "disabled");
+ return ret;
+}
+
status_t MediaRecorder::setOutputFormat(int of)
{
ALOGV("setOutputFormat(%d)", of);
diff --git a/media/libmedia/omx/1.0/WOmx.cpp b/media/libmedia/omx/1.0/WOmx.cpp
index ce624fa..4bacdda 100644
--- a/media/libmedia/omx/1.0/WOmx.cpp
+++ b/media/libmedia/omx/1.0/WOmx.cpp
@@ -18,7 +18,6 @@
#include <media/omx/1.0/WOmx.h>
#include <media/omx/1.0/WOmxNode.h>
#include <media/omx/1.0/WOmxObserver.h>
-#include <media/omx/1.0/WGraphicBufferSource.h>
#include <media/omx/1.0/Conversion.h>
namespace android {
@@ -70,7 +69,7 @@
status_t LWOmx::createInputSurface(
sp<::android::IGraphicBufferProducer>* bufferProducer,
- sp<::android::IGraphicBufferSource>* bufferSource) {
+ sp<::android::hardware::media::omx::V1_0::IGraphicBufferSource>* bufferSource) {
status_t fnStatus;
status_t transStatus = toStatusT(mBase->createInputSurface(
[&fnStatus, bufferProducer, bufferSource] (
@@ -79,7 +78,7 @@
sp<IGraphicBufferSource> const& tSource) {
fnStatus = toStatusT(status);
*bufferProducer = new H2BGraphicBufferProducer(tProducer);
- *bufferSource = new LWGraphicBufferSource(tSource);
+ *bufferSource = tSource;
}));
return transStatus == NO_ERROR ? fnStatus : transStatus;
}
diff --git a/media/libmediahelper/TypeConverter.cpp b/media/libmediahelper/TypeConverter.cpp
index f862018..fc037a6 100644
--- a/media/libmediahelper/TypeConverter.cpp
+++ b/media/libmediahelper/TypeConverter.cpp
@@ -326,6 +326,7 @@
MAKE_STRING_FROM_ENUM(AUDIO_MODE_RINGTONE),
MAKE_STRING_FROM_ENUM(AUDIO_MODE_IN_CALL),
MAKE_STRING_FROM_ENUM(AUDIO_MODE_IN_COMMUNICATION),
+ MAKE_STRING_FROM_ENUM(AUDIO_MODE_CALL_SCREEN),
TERMINATOR
};
@@ -358,6 +359,7 @@
MAKE_STRING_FROM_ENUM(AUDIO_USAGE_GAME),
MAKE_STRING_FROM_ENUM(AUDIO_USAGE_VIRTUAL_SOURCE),
MAKE_STRING_FROM_ENUM(AUDIO_USAGE_ASSISTANT),
+ MAKE_STRING_FROM_ENUM(AUDIO_USAGE_CALL_ASSISTANT),
TERMINATOR
};
@@ -395,6 +397,7 @@
MAKE_STRING_FROM_ENUM(AUDIO_FLAG_DEEP_BUFFER),
MAKE_STRING_FROM_ENUM(AUDIO_FLAG_NO_MEDIA_PROJECTION),
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 15ea578..7fd5408 100644
--- a/media/libmediametrics/Android.bp
+++ b/media/libmediametrics/Android.bp
@@ -2,8 +2,8 @@
name: "libmediametrics",
srcs: [
- "IMediaAnalyticsService.cpp",
- "MediaAnalyticsItem.cpp",
+ "IMediaMetricsService.cpp",
+ "MediaMetricsItem.cpp",
"MediaMetrics.cpp",
],
@@ -37,6 +37,16 @@
"1" ,
]
},
+
+ header_abi_checker: {
+ enabled: true,
+ symbol_file: "libmediametrics.map.txt",
+ },
+
+ visibility: [
+ "//cts/tests/tests/nativemedia/mediametrics",
+ "//frameworks/av:__subpackages__",
+ "//frameworks/base/core/jni",
+ "//frameworks/base/media/jni",
+ ],
}
-
-
diff --git a/media/libmediametrics/IMediaAnalyticsService.cpp b/media/libmediametrics/IMediaAnalyticsService.cpp
deleted file mode 100644
index 9114927..0000000
--- a/media/libmediametrics/IMediaAnalyticsService.cpp
+++ /dev/null
@@ -1,166 +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 "MediaAnalytics"
-
-#include <stdint.h>
-#include <inttypes.h>
-#include <sys/types.h>
-
-#include <binder/Parcel.h>
-#include <binder/IMemory.h>
-#include <binder/IPCThreadState.h>
-
-#include <utils/Errors.h> // for status_t
-#include <utils/List.h>
-#include <utils/Log.h>
-#include <utils/String8.h>
-
-#include <media/MediaAnalyticsItem.h>
-#include <media/IMediaAnalyticsService.h>
-
-#define DEBUGGING 0
-#define DEBUGGING_FLOW 0
-#define DEBUGGING_RETURNS 0
-
-namespace android {
-
-enum {
- GENERATE_UNIQUE_SESSIONID = IBinder::FIRST_CALL_TRANSACTION,
- SUBMIT_ITEM,
-};
-
-class BpMediaAnalyticsService: public BpInterface<IMediaAnalyticsService>
-{
-public:
- explicit BpMediaAnalyticsService(const sp<IBinder>& impl)
- : BpInterface<IMediaAnalyticsService>(impl)
- {
- }
-
- virtual MediaAnalyticsItem::SessionID_t generateUniqueSessionID() {
- Parcel data, reply;
- status_t err;
- MediaAnalyticsItem::SessionID_t sessionid =
- MediaAnalyticsItem::SessionIDInvalid;
-
- data.writeInterfaceToken(IMediaAnalyticsService::getInterfaceDescriptor());
- err = remote()->transact(GENERATE_UNIQUE_SESSIONID, data, &reply);
- if (err != NO_ERROR) {
- ALOGW("bad response from service for generateSessionId, err=%d", err);
- return MediaAnalyticsItem::SessionIDInvalid;
- }
- sessionid = reply.readInt64();
- if (DEBUGGING_RETURNS) {
- ALOGD("the caller gets a sessionid of %" PRId64 " back", sessionid);
- }
- return sessionid;
- }
-
- virtual MediaAnalyticsItem::SessionID_t submit(MediaAnalyticsItem *item, bool forcenew)
- {
- // have this record submit itself
- // this will be a binder call with appropriate timing
- // return value is the uuid that the system generated for it.
- // the return value 0 and -1 are reserved.
- // -1 to indicate that there was a problem recording...
-
- Parcel data, reply;
- status_t err;
-
- if (item == NULL) {
- return MediaAnalyticsItem::SessionIDInvalid;
- }
-
- data.writeInterfaceToken(IMediaAnalyticsService::getInterfaceDescriptor());
- if(DEBUGGING_FLOW) {
- ALOGD("client offers record: %s", item->toString().c_str());
- }
- data.writeBool(forcenew);
- item->writeToParcel(&data);
-
- err = remote()->transact(SUBMIT_ITEM, data, &reply);
- if (err != NO_ERROR) {
- ALOGW("bad response from service for submit, err=%d", err);
- return MediaAnalyticsItem::SessionIDInvalid;
- }
-
- // get an answer out of 'reply'
- int64_t sessionid = reply.readInt64();
- if (DEBUGGING_RETURNS) {
- ALOGD("the caller gets sessionid=%" PRId64 "", sessionid);
- }
- return sessionid;
- }
-
-};
-
-IMPLEMENT_META_INTERFACE(MediaAnalyticsService, "android.media.IMediaAnalyticsService");
-
-// ----------------------------------------------------------------------
-
-status_t BnMediaAnalyticsService::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
-
-
- // get calling pid/tid
- IPCThreadState *ipc = IPCThreadState::self();
- int clientPid = ipc->getCallingPid();
- // permission checking
-
- if(DEBUGGING_FLOW) {
- ALOGD("running in service, code %d, pid %d; called from pid %d",
- code, getpid(), clientPid);
- }
-
- switch (code) {
-
- case GENERATE_UNIQUE_SESSIONID: {
- CHECK_INTERFACE(IMediaAnalyticsService, data, reply);
-
- MediaAnalyticsItem::SessionID_t sessionid = generateUniqueSessionID();
- reply->writeInt64(sessionid);
-
- return NO_ERROR;
- } break;
-
- case SUBMIT_ITEM: {
- CHECK_INTERFACE(IMediaAnalyticsService, data, reply);
-
- bool forcenew;
- MediaAnalyticsItem *item = MediaAnalyticsItem::create();
-
- data.readBool(&forcenew);
- item->readFromParcel(data);
-
- item->setPid(clientPid);
-
- // submit() takes over ownership of 'item'
- MediaAnalyticsItem::SessionID_t sessionid = submit(item, forcenew);
- reply->writeInt64(sessionid);
-
- return NO_ERROR;
- } break;
-
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-// ----------------------------------------------------------------------------
-
-} // namespace android
diff --git a/media/libmediametrics/IMediaMetricsService.cpp b/media/libmediametrics/IMediaMetricsService.cpp
new file mode 100644
index 0000000..b5675e6
--- /dev/null
+++ b/media/libmediametrics/IMediaMetricsService.cpp
@@ -0,0 +1,141 @@
+/*
+ * 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"
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <sys/types.h>
+
+#include <binder/Parcel.h>
+#include <binder/IMemory.h>
+#include <binder/IPCThreadState.h>
+
+#include <utils/Errors.h> // for status_t
+#include <utils/List.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include <media/MediaMetricsItem.h>
+#include <media/IMediaMetricsService.h>
+
+namespace android {
+
+// TODO: Currently ONE_WAY transactions, make both ONE_WAY and synchronous options.
+
+enum {
+ SUBMIT_ITEM = IBinder::FIRST_CALL_TRANSACTION,
+ SUBMIT_BUFFER,
+};
+
+class BpMediaMetricsService: public BpInterface<IMediaMetricsService>
+{
+public:
+ explicit BpMediaMetricsService(const sp<IBinder>& impl)
+ : BpInterface<IMediaMetricsService>(impl)
+ {
+ }
+
+ status_t submit(mediametrics::Item *item) override
+ {
+ if (item == nullptr) {
+ return BAD_VALUE;
+ }
+ ALOGV("%s: (ONEWAY) item=%s", __func__, item->toString().c_str());
+
+ Parcel data;
+ data.writeInterfaceToken(IMediaMetricsService::getInterfaceDescriptor());
+
+ status_t status = item->writeToParcel(&data);
+ if (status != NO_ERROR) { // assume failure logged in item
+ return status;
+ }
+
+ status = remote()->transact(
+ SUBMIT_ITEM, data, nullptr /* reply */, IBinder::FLAG_ONEWAY);
+ ALOGW_IF(status != NO_ERROR, "%s: bad response from service for submit, status=%d",
+ __func__, status);
+ return status;
+ }
+
+ status_t submitBuffer(const char *buffer, size_t length) override
+ {
+ if (buffer == nullptr || length > INT32_MAX) {
+ return BAD_VALUE;
+ }
+ ALOGV("%s: (ONEWAY) length:%zu", __func__, length);
+
+ Parcel data;
+ data.writeInterfaceToken(IMediaMetricsService::getInterfaceDescriptor());
+
+ status_t status = data.writeInt32(length)
+ ?: data.write((uint8_t*)buffer, length);
+ if (status != NO_ERROR) {
+ return status;
+ }
+
+ status = remote()->transact(
+ SUBMIT_BUFFER, data, nullptr /* reply */, IBinder::FLAG_ONEWAY);
+ ALOGW_IF(status != NO_ERROR, "%s: bad response from service for submit, status=%d",
+ __func__, status);
+ return status;
+ }
+};
+
+IMPLEMENT_META_INTERFACE(MediaMetricsService, "android.media.IMediaMetricsService");
+
+// ----------------------------------------------------------------------
+
+status_t BnMediaMetricsService::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch (code) {
+ case SUBMIT_ITEM: {
+ CHECK_INTERFACE(IMediaMetricsService, data, reply);
+
+ mediametrics::Item * const item = mediametrics::Item::create();
+ status_t status = item->readFromParcel(data);
+ if (status != NO_ERROR) { // assume failure logged in item
+ return status;
+ }
+ status = submitInternal(item, true /* release */);
+ // assume failure logged by submitInternal
+ return NO_ERROR;
+ }
+ case SUBMIT_BUFFER: {
+ CHECK_INTERFACE(IMediaMetricsService, data, reply);
+ int32_t length;
+ status_t status = data.readInt32(&length);
+ if (status != NO_ERROR || length <= 0) {
+ return BAD_VALUE;
+ }
+ const void *ptr = data.readInplace(length);
+ if (ptr == nullptr) {
+ return BAD_VALUE;
+ }
+ status = submitBuffer(static_cast<const char *>(ptr), length);
+ // assume failure logged by submitBuffer
+ return NO_ERROR;
+ }
+
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+} // namespace android
diff --git a/media/libmediametrics/MediaAnalyticsItem.cpp b/media/libmediametrics/MediaAnalyticsItem.cpp
deleted file mode 100644
index b7856a6..0000000
--- a/media/libmediametrics/MediaAnalyticsItem.cpp
+++ /dev/null
@@ -1,1250 +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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "MediaAnalyticsItem"
-
-#include <inttypes.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-
-#include <binder/Parcel.h>
-#include <utils/Errors.h>
-#include <utils/Log.h>
-#include <utils/Mutex.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>
-
-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
-
-// the few universal keys we have
-const MediaAnalyticsItem::Key MediaAnalyticsItem::kKeyAny = "any";
-const MediaAnalyticsItem::Key MediaAnalyticsItem::kKeyNone = "none";
-
-const char * const MediaAnalyticsItem::EnabledProperty = "media.metrics.enabled";
-const char * const MediaAnalyticsItem::EnabledPropertyPersist = "persist.media.metrics.enabled";
-const int MediaAnalyticsItem::EnabledProperty_default = 1;
-
-// So caller doesn't need to know size of allocated space
-MediaAnalyticsItem *MediaAnalyticsItem::create()
-{
- return MediaAnalyticsItem::create(kKeyNone);
-}
-
-MediaAnalyticsItem *MediaAnalyticsItem::create(MediaAnalyticsItem::Key key)
-{
- MediaAnalyticsItem *item = new MediaAnalyticsItem(key);
- return item;
-}
-
-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;
-}
-
-// access functions for the class
-MediaAnalyticsItem::MediaAnalyticsItem()
- : mPid(-1),
- mUid(-1),
- mPkgVersionCode(0),
- mSessionID(MediaAnalyticsItem::SessionIDNone),
- mTimestamp(0),
- mFinalized(1),
- mPropCount(0), mPropSize(0), mProps(NULL)
-{
- mKey = MediaAnalyticsItem::kKeyNone;
-}
-
-MediaAnalyticsItem::MediaAnalyticsItem(MediaAnalyticsItem::Key key)
- : mPid(-1),
- mUid(-1),
- mPkgVersionCode(0),
- mSessionID(MediaAnalyticsItem::SessionIDNone),
- mTimestamp(0),
- mFinalized(1),
- mPropCount(0), mPropSize(0), mProps(NULL)
-{
- if (DEBUG_ALLOCATIONS) {
- ALOGD("Allocate MediaAnalyticsItem @ %p", this);
- }
- mKey = key;
-}
-
-MediaAnalyticsItem::~MediaAnalyticsItem() {
- if (DEBUG_ALLOCATIONS) {
- ALOGD("Destroy MediaAnalyticsItem @ %p", this);
- }
- clear();
-}
-
-void MediaAnalyticsItem::clear() {
-
- // clean allocated storage from key
- mKey.clear();
-
- // clean various major parameters
- mSessionID = MediaAnalyticsItem::SessionIDNone;
-
- // clean attributes
- // contents of the attributes
- for (size_t i = 0 ; i < mPropCount; i++ ) {
- clearProp(&mProps[i]);
- }
- // 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->mSessionID = this->mSessionID;
- dst->mTimestamp = this->mTimestamp;
- dst->mFinalized = this->mFinalized;
-
- // properties aka attributes
- dst->growProps(this->mPropCount);
- for(size_t i=0;i<mPropCount;i++) {
- copyProp(&dst->mProps[i], &this->mProps[i]);
- }
- dst->mPropCount = this->mPropCount;
- }
-
- return dst;
-}
-
-MediaAnalyticsItem &MediaAnalyticsItem::setSessionID(MediaAnalyticsItem::SessionID_t id) {
- mSessionID = id;
- return *this;
-}
-
-MediaAnalyticsItem::SessionID_t MediaAnalyticsItem::getSessionID() const {
- return mSessionID;
-}
-
-MediaAnalyticsItem::SessionID_t MediaAnalyticsItem::generateSessionID() {
-
- if (mSessionID == SessionIDNone) {
- // get one from the server
- MediaAnalyticsItem::SessionID_t newid = SessionIDNone;
- sp<IMediaAnalyticsService> svc = getInstance();
- if (svc != NULL) {
- newid = svc->generateUniqueSessionID();
- }
- mSessionID = newid;
- }
-
- return mSessionID;
-}
-
-MediaAnalyticsItem &MediaAnalyticsItem::clearSessionID() {
- mSessionID = MediaAnalyticsItem::SessionIDNone;
- return *this;
-}
-
-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;
-}
-
-// this key is for the overall record -- "codec", "player", "drm", etc
-MediaAnalyticsItem &MediaAnalyticsItem::setKey(MediaAnalyticsItem::Key key) {
- mKey = key;
- return *this;
-}
-
-MediaAnalyticsItem::Key MediaAnalyticsItem::getKey() {
- return mKey;
-}
-
-// number of attributes we have in this record
-int32_t MediaAnalyticsItem::count() const {
- return mPropCount;
-}
-
-// find the proper entry in the list
-size_t MediaAnalyticsItem::findPropIndex(const char *name, size_t len)
-{
- size_t i = 0;
- for (; i < mPropCount; i++) {
- Prop *prop = &mProps[i];
- if (prop->mNameLen != len) {
- continue;
- }
- if (memcmp(name, prop->mName, len) == 0) {
- break;
- }
- }
- return i;
-}
-
-MediaAnalyticsItem::Prop *MediaAnalyticsItem::findProp(const char *name) {
- size_t len = strlen(name);
- size_t i = findPropIndex(name, len);
- if (i < mPropCount) {
- return &mProps[i];
- }
- return NULL;
-}
-
-void MediaAnalyticsItem::Prop::setName(const char *name, size_t len) {
- free((void *)mName);
- mName = (const char *) malloc(len+1);
- LOG_ALWAYS_FATAL_IF(mName == NULL,
- "failed malloc() for property '%s' (len %zu)",
- name, len);
- memcpy ((void *)mName, name, len+1);
- mNameLen = len;
-}
-
-// consider this "find-or-allocate".
-// caller validates type and uses clearPropValue() accordingly
-MediaAnalyticsItem::Prop *MediaAnalyticsItem::allocateProp(const char *name) {
- size_t len = strlen(name);
- size_t i = findPropIndex(name, len);
- Prop *prop;
-
- if (i < mPropCount) {
- prop = &mProps[i];
- } else {
- if (i == mPropSize) {
- if (growProps() == false) {
- ALOGE("failed allocation for new props");
- return NULL;
- }
- }
- i = mPropCount++;
- prop = &mProps[i];
- prop->setName(name, len);
- }
-
- return prop;
-}
-
-// used within the summarizers; return whether property existed
-bool MediaAnalyticsItem::removeProp(const char *name) {
- size_t len = strlen(name);
- size_t i = findPropIndex(name, len);
- if (i < mPropCount) {
- Prop *prop = &mProps[i];
- clearProp(prop);
- if (i != mPropCount-1) {
- // in the middle, bring last one down to fill gap
- copyProp(prop, &mProps[mPropCount-1]);
- clearProp(&mProps[mPropCount-1]);
- }
- mPropCount--;
- return true;
- }
- return false;
-}
-
-// set the values
-void MediaAnalyticsItem::setInt32(MediaAnalyticsItem::Attr name, int32_t value) {
- Prop *prop = allocateProp(name);
- if (prop != NULL) {
- clearPropValue(prop);
- prop->mType = kTypeInt32;
- prop->u.int32Value = value;
- }
-}
-
-void MediaAnalyticsItem::setInt64(MediaAnalyticsItem::Attr name, int64_t value) {
- Prop *prop = allocateProp(name);
- if (prop != NULL) {
- clearPropValue(prop);
- prop->mType = kTypeInt64;
- prop->u.int64Value = value;
- }
-}
-
-void MediaAnalyticsItem::setDouble(MediaAnalyticsItem::Attr name, double value) {
- Prop *prop = allocateProp(name);
- if (prop != NULL) {
- clearPropValue(prop);
- prop->mType = kTypeDouble;
- prop->u.doubleValue = value;
- }
-}
-
-void MediaAnalyticsItem::setCString(MediaAnalyticsItem::Attr name, const char *value) {
-
- Prop *prop = allocateProp(name);
- // any old value will be gone
- if (prop != NULL) {
- clearPropValue(prop);
- prop->mType = kTypeCString;
- prop->u.CStringValue = strdup(value);
- }
-}
-
-void MediaAnalyticsItem::setRate(MediaAnalyticsItem::Attr name, int64_t count, int64_t duration) {
- Prop *prop = allocateProp(name);
- if (prop != NULL) {
- clearPropValue(prop);
- prop->mType = kTypeRate;
- prop->u.rate.count = count;
- prop->u.rate.duration = duration;
- }
-}
-
-
-// find/add/set fused into a single operation
-void MediaAnalyticsItem::addInt32(MediaAnalyticsItem::Attr name, int32_t value) {
- Prop *prop = allocateProp(name);
- if (prop == NULL) {
- return;
- }
- switch (prop->mType) {
- case kTypeInt32:
- prop->u.int32Value += value;
- break;
- default:
- clearPropValue(prop);
- prop->mType = kTypeInt32;
- prop->u.int32Value = value;
- break;
- }
-}
-
-void MediaAnalyticsItem::addInt64(MediaAnalyticsItem::Attr name, int64_t value) {
- Prop *prop = allocateProp(name);
- if (prop == NULL) {
- return;
- }
- switch (prop->mType) {
- case kTypeInt64:
- prop->u.int64Value += value;
- break;
- default:
- clearPropValue(prop);
- prop->mType = kTypeInt64;
- prop->u.int64Value = value;
- break;
- }
-}
-
-void MediaAnalyticsItem::addRate(MediaAnalyticsItem::Attr name, int64_t count, int64_t duration) {
- Prop *prop = allocateProp(name);
- if (prop == NULL) {
- return;
- }
- switch (prop->mType) {
- case kTypeRate:
- prop->u.rate.count += count;
- prop->u.rate.duration += duration;
- break;
- default:
- clearPropValue(prop);
- prop->mType = kTypeRate;
- prop->u.rate.count = count;
- prop->u.rate.duration = duration;
- break;
- }
-}
-
-void MediaAnalyticsItem::addDouble(MediaAnalyticsItem::Attr name, double value) {
- Prop *prop = allocateProp(name);
- if (prop == NULL) {
- return;
- }
- switch (prop->mType) {
- case kTypeDouble:
- prop->u.doubleValue += value;
- break;
- default:
- clearPropValue(prop);
- prop->mType = kTypeDouble;
- prop->u.doubleValue = value;
- break;
- }
-}
-
-// find & extract values
-bool MediaAnalyticsItem::getInt32(MediaAnalyticsItem::Attr name, int32_t *value) {
- Prop *prop = findProp(name);
- if (prop == NULL || prop->mType != kTypeInt32) {
- return false;
- }
- if (value != NULL) {
- *value = prop->u.int32Value;
- }
- return true;
-}
-
-bool MediaAnalyticsItem::getInt64(MediaAnalyticsItem::Attr name, int64_t *value) {
- Prop *prop = findProp(name);
- if (prop == NULL || prop->mType != kTypeInt64) {
- return false;
- }
- if (value != NULL) {
- *value = prop->u.int64Value;
- }
- return true;
-}
-
-bool MediaAnalyticsItem::getRate(MediaAnalyticsItem::Attr name, int64_t *count, int64_t *duration, double *rate) {
- Prop *prop = findProp(name);
- if (prop == NULL || prop->mType != kTypeRate) {
- return false;
- }
- if (count != NULL) {
- *count = prop->u.rate.count;
- }
- if (duration != NULL) {
- *duration = prop->u.rate.duration;
- }
- if (rate != NULL) {
- double r = 0.0;
- if (prop->u.rate.duration != 0) {
- r = prop->u.rate.count / (double) prop->u.rate.duration;
- }
- *rate = r;
- }
- return true;
-}
-
-bool MediaAnalyticsItem::getDouble(MediaAnalyticsItem::Attr name, double *value) {
- Prop *prop = findProp(name);
- if (prop == NULL || prop->mType != kTypeDouble) {
- return false;
- }
- if (value != NULL) {
- *value = prop->u.doubleValue;
- }
- return true;
-}
-
-// caller responsible for the returned string
-bool MediaAnalyticsItem::getCString(MediaAnalyticsItem::Attr name, char **value) {
- Prop *prop = findProp(name);
- if (prop == NULL || prop->mType != kTypeCString) {
- return false;
- }
- if (value != NULL) {
- *value = strdup(prop->u.CStringValue);
- }
- return true;
-}
-
-bool MediaAnalyticsItem::getString(MediaAnalyticsItem::Attr name, std::string *value) {
- Prop *prop = findProp(name);
- if (prop == NULL || prop->mType != kTypeCString) {
- return false;
- }
- if (value != NULL) {
- // std::string makes a copy for us
- *value = prop->u.CStringValue;
- }
- return true;
-}
-
-// remove indicated keys and their values
-// return value is # keys removed
-int32_t MediaAnalyticsItem::filter(int n, MediaAnalyticsItem::Attr attrs[]) {
- int zapped = 0;
- if (attrs == NULL || n <= 0) {
- return -1;
- }
- for (ssize_t i = 0 ; i < n ; i++) {
- const char *name = attrs[i];
- size_t len = strlen(name);
- size_t j = findPropIndex(name, len);
- if (j >= mPropCount) {
- // not there
- continue;
- } else if (j+1 == mPropCount) {
- // last one, shorten
- zapped++;
- clearProp(&mProps[j]);
- mPropCount--;
- } else {
- // in the middle, bring last one down and shorten
- zapped++;
- clearProp(&mProps[j]);
- mProps[j] = mProps[mPropCount-1];
- mPropCount--;
- }
- }
- return zapped;
-}
-
-// remove any keys NOT in the provided list
-// return value is # keys removed
-int32_t MediaAnalyticsItem::filterNot(int n, MediaAnalyticsItem::Attr attrs[]) {
- int zapped = 0;
- if (attrs == NULL || n <= 0) {
- return -1;
- }
- for (ssize_t i = mPropCount-1 ; i >=0 ; i--) {
- Prop *prop = &mProps[i];
- for (ssize_t j = 0; j < n ; j++) {
- if (strcmp(prop->mName, attrs[j]) == 0) {
- clearProp(prop);
- zapped++;
- if (i != (ssize_t)(mPropCount-1)) {
- *prop = mProps[mPropCount-1];
- }
- initProp(&mProps[mPropCount-1]);
- mPropCount--;
- break;
- }
- }
- }
- return zapped;
-}
-
-// remove a single key
-// return value is 0 (not found) or 1 (found and removed)
-int32_t MediaAnalyticsItem::filter(MediaAnalyticsItem::Attr name) {
- return filter(1, &name);
-}
-
-// handle individual items/properties stored within the class
-//
-
-void MediaAnalyticsItem::initProp(Prop *prop) {
- if (prop != NULL) {
- prop->mName = NULL;
- prop->mNameLen = 0;
-
- prop->mType = kTypeNone;
- }
-}
-
-void MediaAnalyticsItem::clearProp(Prop *prop)
-{
- if (prop != NULL) {
- if (prop->mName != NULL) {
- free((void *)prop->mName);
- prop->mName = NULL;
- prop->mNameLen = 0;
- }
-
- clearPropValue(prop);
- }
-}
-
-void MediaAnalyticsItem::clearPropValue(Prop *prop)
-{
- if (prop != NULL) {
- if (prop->mType == kTypeCString && prop->u.CStringValue != NULL) {
- free(prop->u.CStringValue);
- prop->u.CStringValue = NULL;
- }
- prop->mType = kTypeNone;
- }
-}
-
-void MediaAnalyticsItem::copyProp(Prop *dst, const Prop *src)
-{
- // get rid of any pointers in the dst
- clearProp(dst);
-
- *dst = *src;
-
- // fix any pointers that we blindly copied, so we have our own copies
- if (dst->mName) {
- void *p = malloc(dst->mNameLen + 1);
- LOG_ALWAYS_FATAL_IF(p == NULL,
- "failed malloc() duping property '%s' (len %zu)",
- dst->mName, dst->mNameLen);
- memcpy (p, src->mName, dst->mNameLen + 1);
- dst->mName = (const char *) p;
- }
- if (dst->mType == kTypeCString) {
- dst->u.CStringValue = strdup(src->u.CStringValue);
- }
-}
-
-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++) {
- initProp(&ni[i]);
- }
- mProps = ni;
- mPropSize = nsize;
- return true;
- } else {
- ALOGW("MediaAnalyticsItem::growProps fails");
- return false;
- }
-}
-
-// Parcel / serialize things for binder calls
-//
-
-int32_t MediaAnalyticsItem::readFromParcel(const Parcel& data) {
- int32_t version = data.readInt32();
-
- switch(version) {
- case 0:
- return readFromParcel0(data);
- break;
- default:
- ALOGE("Unsupported MediaAnalyticsItem Parcel version: %d", version);
- return -1;
- }
-}
-
-int32_t MediaAnalyticsItem::readFromParcel0(const Parcel& data) {
- // into 'this' object
- // .. we make a copy of the string to put away.
- mKey = data.readCString();
- mPid = data.readInt32();
- mUid = data.readInt32();
- mPkgName = data.readCString();
- mPkgVersionCode = data.readInt64();
- mSessionID = data.readInt64();
- // We no longer pay attention to user setting of finalized, BUT it's
- // still part of the wire packet -- so read & discard.
- mFinalized = data.readInt32();
- mFinalized = 1;
- mTimestamp = data.readInt64();
-
- int count = data.readInt32();
- for (int i = 0; i < count ; i++) {
- MediaAnalyticsItem::Attr attr = data.readCString();
- int32_t ztype = data.readInt32();
- switch (ztype) {
- case MediaAnalyticsItem::kTypeInt32:
- setInt32(attr, data.readInt32());
- break;
- case MediaAnalyticsItem::kTypeInt64:
- setInt64(attr, data.readInt64());
- break;
- case MediaAnalyticsItem::kTypeDouble:
- setDouble(attr, data.readDouble());
- break;
- case MediaAnalyticsItem::kTypeCString:
- setCString(attr, data.readCString());
- break;
- case MediaAnalyticsItem::kTypeRate:
- {
- int64_t count = data.readInt64();
- int64_t duration = data.readInt64();
- setRate(attr, count, duration);
- }
- break;
- default:
- ALOGE("reading bad item type: %d, idx %d",
- ztype, i);
- return -1;
- }
- }
-
- return 0;
-}
-
-int32_t MediaAnalyticsItem::writeToParcel(Parcel *data) {
-
- if (data == NULL) return -1;
-
- int32_t version = 0;
- data->writeInt32(version);
-
- switch(version) {
- case 0:
- return writeToParcel0(data);
- break;
- default:
- ALOGE("Unsupported MediaAnalyticsItem Parcel version: %d", version);
- return -1;
- }
-}
-
-int32_t MediaAnalyticsItem::writeToParcel0(Parcel *data) {
-
- data->writeCString(mKey.c_str());
- data->writeInt32(mPid);
- data->writeInt32(mUid);
- data->writeCString(mPkgName.c_str());
- data->writeInt64(mPkgVersionCode);
- data->writeInt64(mSessionID);
- data->writeInt32(mFinalized);
- data->writeInt64(mTimestamp);
-
- // set of items
- int count = mPropCount;
- data->writeInt32(count);
- for (int i = 0 ; i < count; i++ ) {
- Prop *prop = &mProps[i];
- data->writeCString(prop->mName);
- data->writeInt32(prop->mType);
- switch (prop->mType) {
- case MediaAnalyticsItem::kTypeInt32:
- data->writeInt32(prop->u.int32Value);
- break;
- case MediaAnalyticsItem::kTypeInt64:
- data->writeInt64(prop->u.int64Value);
- break;
- case MediaAnalyticsItem::kTypeDouble:
- data->writeDouble(prop->u.doubleValue);
- break;
- case MediaAnalyticsItem::kTypeRate:
- data->writeInt64(prop->u.rate.count);
- data->writeInt64(prop->u.rate.duration);
- break;
- case MediaAnalyticsItem::kTypeCString:
- data->writeCString(prop->u.CStringValue);
- break;
- default:
- ALOGE("found bad Prop type: %d, idx %d, name %s",
- prop->mType, i, prop->mName);
- break;
- }
- }
-
- return 0;
-}
-
-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() {
- return toString(PROTO_LAST);
-}
-
-std::string MediaAnalyticsItem::toString(int version) {
-
- // v0 : released with 'o'
- // v1 : bug fix (missing pid/finalized separator),
- // adds apk name, apk version code
-
- if (version <= PROTO_FIRST) {
- // default to original v0 format, until proper parsers are in place
- version = PROTO_V0;
- } else if (version > PROTO_LAST) {
- version = PROTO_LAST;
- }
-
- std::string result;
- char buffer[512];
-
- if (version == PROTO_V0) {
- result = "(";
- } else {
- snprintf(buffer, sizeof(buffer), "[%d:", version);
- result.append(buffer);
- }
-
- // same order as we spill into the parcel, although not required
- // key+session are our primary matching criteria
- result.append(mKey.c_str());
- result.append(":");
- snprintf(buffer, sizeof(buffer), "%" PRId64 ":", mSessionID);
- result.append(buffer);
-
- snprintf(buffer, sizeof(buffer), "%d:", mUid);
- result.append(buffer);
-
- if (version >= PROTO_V1) {
- result.append(mPkgName);
- snprintf(buffer, sizeof(buffer), ":%" PRId64 ":", mPkgVersionCode);
- result.append(buffer);
- }
-
- // in 'o' (v1) , the separator between pid and finalized was omitted
- if (version <= PROTO_V0) {
- snprintf(buffer, sizeof(buffer), "%d", mPid);
- } else {
- snprintf(buffer, sizeof(buffer), "%d:", mPid);
- }
- result.append(buffer);
-
- snprintf(buffer, sizeof(buffer), "%d:", mFinalized);
- result.append(buffer);
- snprintf(buffer, sizeof(buffer), "%" PRId64 ":", mTimestamp);
- result.append(buffer);
-
- // set of items
- int count = mPropCount;
- snprintf(buffer, sizeof(buffer), "%d:", count);
- result.append(buffer);
- for (int i = 0 ; i < count; i++ ) {
- Prop *prop = &mProps[i];
- switch (prop->mType) {
- case MediaAnalyticsItem::kTypeInt32:
- snprintf(buffer,sizeof(buffer),
- "%s=%d:", prop->mName, prop->u.int32Value);
- break;
- case MediaAnalyticsItem::kTypeInt64:
- snprintf(buffer,sizeof(buffer),
- "%s=%" PRId64 ":", prop->mName, prop->u.int64Value);
- break;
- case MediaAnalyticsItem::kTypeDouble:
- snprintf(buffer,sizeof(buffer),
- "%s=%e:", prop->mName, prop->u.doubleValue);
- break;
- case MediaAnalyticsItem::kTypeRate:
- snprintf(buffer,sizeof(buffer),
- "%s=%" PRId64 "/%" PRId64 ":", prop->mName,
- prop->u.rate.count, prop->u.rate.duration);
- break;
- case MediaAnalyticsItem::kTypeCString:
- snprintf(buffer,sizeof(buffer), "%s=", prop->mName);
- result.append(buffer);
- // XXX: sanitize string for ':' '='
- result.append(prop->u.CStringValue);
- buffer[0] = ':';
- buffer[1] = '\0';
- break;
- default:
- ALOGE("to_String bad item type: %d for %s",
- prop->mType, prop->mName);
- break;
- }
- result.append(buffer);
- }
-
- if (version == PROTO_V0) {
- result.append(")");
- } else {
- result.append("]");
- }
-
- return result;
-}
-
-// for the lazy, we offer methods that finds the service and
-// calls the appropriate daemon
-bool MediaAnalyticsItem::selfrecord() {
- return selfrecord(false);
-}
-
-bool MediaAnalyticsItem::selfrecord(bool forcenew) {
-
- if (DEBUG_API) {
- std::string p = this->toString();
- ALOGD("selfrecord of: %s [forcenew=%d]", p.c_str(), forcenew);
- }
-
- sp<IMediaAnalyticsService> svc = getInstance();
-
- if (svc != NULL) {
- MediaAnalyticsItem::SessionID_t newid = svc->submit(this, forcenew);
- if (newid == SessionIDInvalid) {
- std::string p = this->toString();
- ALOGW("Failed to record: %s [forcenew=%d]", p.c_str(), forcenew);
- return false;
- }
- return true;
- } else {
- return false;
- }
-}
-
-// get a connection we can reuse for most of our lifetime
-// static
-sp<IMediaAnalyticsService> MediaAnalyticsItem::sAnalyticsService;
-static Mutex sInitMutex;
-static int remainingBindAttempts = SVC_TRIES;
-
-//static
-bool MediaAnalyticsItem::isEnabled() {
- 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;
- }
- if (enabled <= 0) {
- return false;
- }
- return true;
-}
-
-
-// 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");
- MediaAnalyticsItem::dropInstance();
- }
-};
-
-static sp<MediaMetricsDeathNotifier> sNotifier = NULL;
-
-// static
-void MediaAnalyticsItem::dropInstance() {
- Mutex::Autolock _l(sInitMutex);
- remainingBindAttempts = SVC_TRIES;
- sAnalyticsService = NULL;
-}
-
-//static
-sp<IMediaAnalyticsService> MediaAnalyticsItem::getInstance() {
-
- static const char *servicename = "media.metrics";
- int enabled = isEnabled();
-
- if (enabled == false) {
- if (DEBUG_SERVICEACCESS) {
- ALOGD("disabled");
- }
- return NULL;
- }
-
- // 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.
- {
- uid_t uid = getuid();
- switch (uid) {
- case AID_RADIO: // telephony subsystem, RIL
- return NULL;
- break;
- default:
- // let sepolicy deny access if appropriate
- break;
- }
- }
-
- {
- Mutex::Autolock _l(sInitMutex);
- const char *badness = "";
-
- // think of remainingBindAttempts as telling us whether service==NULL because
- // (1) we haven't tried to initialize it yet
- // (2) we've tried to initialize it, but failed.
- if (sAnalyticsService == NULL && remainingBindAttempts > 0) {
- sp<IServiceManager> sm = defaultServiceManager();
- if (sm != NULL) {
- sp<IBinder> binder = sm->getService(String16(servicename));
- if (binder != NULL) {
- sAnalyticsService = interface_cast<IMediaAnalyticsService>(binder);
- if (sNotifier != NULL) {
- sNotifier = NULL;
- }
- sNotifier = new MediaMetricsDeathNotifier();
- binder->linkToDeath(sNotifier);
- } else {
- badness = "did not find service";
- }
- } else {
- badness = "No Service Manager access";
- }
-
- if (sAnalyticsService == NULL) {
- if (remainingBindAttempts > 0) {
- remainingBindAttempts--;
- }
- if (DEBUG_SERVICEACCESS) {
- ALOGD("Unable to bind to service %s: %s", servicename, badness);
- }
- }
- }
-
- return sAnalyticsService;
- }
-}
-
-// 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;
- } else if (mSessionID == 0) {
- mSessionID = incoming->mSessionID;
- }
-
- // 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) {
- copyProp(oprop, iprop);
- } else {
- ALOGW("dropped property '%s'", iprop->mName);
- }
- } else {
- copyProp(oprop, iprop);
- }
- }
-
- // not sure when we'd return false...
- return true;
-}
-
-// a byte array; contents are
-// overall length (uint32) including the length field itself
-// encoding version (uint32)
-// count of properties (uint32)
-// N copies of:
-// property name as length(int16), bytes
-// the bytes WILL include the null terminator of the name
-// type (uint8 -- 1 byte)
-// size of value field (int16 -- 2 bytes)
-// value (size based on type)
-// int32, int64, double -- little endian 4/8/8 bytes respectively
-// cstring -- N bytes of value [WITH terminator]
-
-enum { kInt32 = 0, kInt64, kDouble, kRate, kCString};
-
-bool MediaAnalyticsItem::dumpAttributes(char **pbuffer, size_t *plength) {
-
- char *build = NULL;
-
- if (pbuffer == NULL || plength == NULL)
- return false;
-
- // consistency for the caller, who owns whatever comes back in this pointer.
- *pbuffer = NULL;
-
- // first, let's calculate sizes
- int32_t goal = 0;
- int32_t version = 0;
-
- goal += sizeof(uint32_t); // overall length, including the length field
- goal += sizeof(uint32_t); // encoding version
- goal += sizeof(uint32_t); // # properties
-
- int32_t count = mPropCount;
- for (int i = 0 ; i < count; i++ ) {
- Prop *prop = &mProps[i];
- goal += sizeof(uint16_t); // name length
- goal += strlen(prop->mName) + 1; // string + null
- goal += sizeof(uint8_t); // type
- goal += sizeof(uint16_t); // size of value
- switch (prop->mType) {
- case MediaAnalyticsItem::kTypeInt32:
- goal += sizeof(uint32_t);
- break;
- case MediaAnalyticsItem::kTypeInt64:
- goal += sizeof(uint64_t);
- break;
- case MediaAnalyticsItem::kTypeDouble:
- goal += sizeof(double);
- break;
- case MediaAnalyticsItem::kTypeRate:
- goal += 2 * sizeof(uint64_t);
- break;
- case MediaAnalyticsItem::kTypeCString:
- // length + actual string + null
- goal += strlen(prop->u.CStringValue) + 1;
- break;
- default:
- ALOGE("found bad Prop type: %d, idx %d, name %s",
- prop->mType, i, prop->mName);
- return false;
- }
- }
-
- // now that we have a size... let's allocate and fill
- build = (char *)malloc(goal);
- if (build == NULL)
- return false;
-
- memset(build, 0, goal);
-
- char *filling = build;
-
-#define _INSERT(val, size) \
- { memcpy(filling, &(val), (size)); filling += (size);}
-#define _INSERTSTRING(val, size) \
- { memcpy(filling, (val), (size)); filling += (size);}
-
- _INSERT(goal, sizeof(int32_t));
- _INSERT(version, sizeof(int32_t));
- _INSERT(count, sizeof(int32_t));
-
- for (int i = 0 ; i < count; i++ ) {
- Prop *prop = &mProps[i];
- int16_t attrNameLen = strlen(prop->mName) + 1;
- _INSERT(attrNameLen, sizeof(int16_t));
- _INSERTSTRING(prop->mName, attrNameLen); // termination included
- int8_t elemtype;
- int16_t elemsize;
- switch (prop->mType) {
- case MediaAnalyticsItem::kTypeInt32:
- {
- elemtype = kInt32;
- _INSERT(elemtype, sizeof(int8_t));
- elemsize = sizeof(int32_t);
- _INSERT(elemsize, sizeof(int16_t));
-
- _INSERT(prop->u.int32Value, sizeof(int32_t));
- break;
- }
- case MediaAnalyticsItem::kTypeInt64:
- {
- elemtype = kInt64;
- _INSERT(elemtype, sizeof(int8_t));
- elemsize = sizeof(int64_t);
- _INSERT(elemsize, sizeof(int16_t));
-
- _INSERT(prop->u.int64Value, sizeof(int64_t));
- break;
- }
- case MediaAnalyticsItem::kTypeDouble:
- {
- elemtype = kDouble;
- _INSERT(elemtype, sizeof(int8_t));
- elemsize = sizeof(double);
- _INSERT(elemsize, sizeof(int16_t));
-
- _INSERT(prop->u.doubleValue, sizeof(double));
- break;
- }
- case MediaAnalyticsItem::kTypeRate:
- {
- elemtype = kRate;
- _INSERT(elemtype, sizeof(int8_t));
- elemsize = 2 * sizeof(uint64_t);
- _INSERT(elemsize, sizeof(int16_t));
-
- _INSERT(prop->u.rate.count, sizeof(uint64_t));
- _INSERT(prop->u.rate.duration, sizeof(uint64_t));
- break;
- }
- case MediaAnalyticsItem::kTypeCString:
- {
- elemtype = kCString;
- _INSERT(elemtype, sizeof(int8_t));
- elemsize = strlen(prop->u.CStringValue) + 1;
- _INSERT(elemsize, sizeof(int16_t));
-
- _INSERTSTRING(prop->u.CStringValue, elemsize);
- break;
- }
- default:
- // error if can't encode; warning if can't decode
- ALOGE("found bad Prop type: %d, idx %d, name %s",
- prop->mType, i, prop->mName);
- goto badness;
- }
- }
-
- if (build + goal != filling) {
- ALOGE("problems populating; wrote=%d planned=%d",
- (int)(filling-build), goal);
- goto badness;
- }
-
- *pbuffer = build;
- *plength = goal;
-
- return true;
-
- badness:
- free(build);
- return false;
-}
-
-} // namespace android
-
diff --git a/media/libmediametrics/MediaMetrics.cpp b/media/libmediametrics/MediaMetrics.cpp
index 360ae0c..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,37 +167,37 @@
}
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->dumpAttributes(buffer, length);
+ 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..62af0f7
--- /dev/null
+++ b/media/libmediametrics/MediaMetricsItem.cpp
@@ -0,0 +1,607 @@
+/*
+ * 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() {
+ return toCString(PROTO_LAST);
+}
+
+const char * mediametrics::Item::toCString(int version) {
+ std::string val = toString(version);
+ return strdup(val.c_str());
+}
+
+std::string mediametrics::Item::toString() const {
+ return toString(PROTO_LAST);
+}
+
+std::string mediametrics::Item::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(), mProps.size());
+ result.append(buffer);
+ for (auto &prop : *this) {
+ prop.toStringBuffer(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 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/TEST_MAPPING b/media/libmediametrics/TEST_MAPPING
new file mode 100644
index 0000000..01e9e46
--- /dev/null
+++ b/media/libmediametrics/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+ "presubmit": [
+ {
+ "name": "mediametrics_tests"
+ },
+ {
+ "name": "CtsNativeMediaMetricsTestCases"
+ }
+ ]
+}
diff --git a/media/libmediametrics/include/IMediaAnalyticsService.h b/media/libmediametrics/include/IMediaAnalyticsService.h
deleted file mode 100644
index f635e94..0000000
--- a/media/libmediametrics/include/IMediaAnalyticsService.h
+++ /dev/null
@@ -1,70 +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_IMEDIAANALYTICSSERVICE_H
-#define ANDROID_IMEDIAANALYTICSSERVICE_H
-
-#include <utils/String8.h>
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-
-#include <sys/types.h>
-#include <utils/Errors.h>
-#include <utils/Log.h>
-#include <utils/RefBase.h>
-#include <utils/List.h>
-
-#include <binder/IServiceManager.h>
-
-#include <media/MediaAnalyticsItem.h>
-// nope...#include <media/MediaAnalytics.h>
-
-namespace android {
-
-class IMediaAnalyticsService: public IInterface
-{
-public:
- DECLARE_META_INTERFACE(MediaAnalyticsService);
-
- // generate a unique sessionID to use across multiple requests
- // 'unique' is within this device, since last reboot
- virtual MediaAnalyticsItem::SessionID_t generateUniqueSessionID() = 0;
-
- // submit the indicated record to the mediaanalytics service, where
- // it will be merged (if appropriate) with incomplete records that
- // share the same key and sessionid.
- // 'forcenew' marks any matching incomplete record as complete before
- // inserting this new record.
- // returns the sessionID associated with that item.
- // caller continues to own the passed item
- virtual MediaAnalyticsItem::SessionID_t submit(MediaAnalyticsItem *item, bool forcenew) = 0;
-
-};
-
-// ----------------------------------------------------------------------------
-
-class BnMediaAnalyticsService: public BnInterface<IMediaAnalyticsService>
-{
-public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-}; // namespace android
-
-#endif // ANDROID_IMEDIASTATISTICSSERVICE_H
diff --git a/media/libmediametrics/include/IMediaMetricsService.h b/media/libmediametrics/include/IMediaMetricsService.h
new file mode 100644
index 0000000..d6871ec
--- /dev/null
+++ b/media/libmediametrics/include/IMediaMetricsService.h
@@ -0,0 +1,73 @@
+/*
+ * 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_IMEDIAANALYTICSSERVICE_H
+#define ANDROID_IMEDIAANALYTICSSERVICE_H
+
+#include <utils/String8.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+#include <sys/types.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <utils/RefBase.h>
+#include <utils/List.h>
+
+#include <binder/IServiceManager.h>
+
+#include <media/MediaMetricsItem.h>
+
+namespace android {
+
+class IMediaMetricsService: public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(MediaMetricsService);
+
+ /**
+ * Submits the indicated record to the mediaanalytics service, where
+ * it will be merged (if appropriate) with incomplete records that
+ * share the same key and sessionID.
+ *
+ * \param item the item to submit.
+ * \return status which is negative if an error is detected (some errors
+ may be silent and return 0 - success).
+ */
+ virtual status_t submit(mediametrics::Item *item) = 0;
+
+ virtual status_t submitBuffer(const char *buffer, size_t length) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnMediaMetricsService: public BnInterface<IMediaMetricsService>
+{
+public:
+ status_t onTransact(uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0) override;
+
+protected:
+ // Internal call where release is true if the service is to delete the item.
+ virtual status_t submitInternal(
+ mediametrics::Item *item, bool release) = 0;
+};
+
+}; // namespace android
+
+#endif // ANDROID_IMEDIASTATISTICSSERVICE_H
diff --git a/media/libmediametrics/include/MediaAnalyticsItem.h b/media/libmediametrics/include/MediaAnalyticsItem.h
deleted file mode 100644
index 42a2f5b..0000000
--- a/media/libmediametrics/include/MediaAnalyticsItem.h
+++ /dev/null
@@ -1,277 +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 <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;
-
-// the class interface
-//
-
-class MediaAnalyticsItem {
-
- friend class MediaAnalyticsService;
- friend class IMediaAnalyticsService;
- friend class MediaMetricsJNI;
- friend class MetricsSummarizer;
- friend class MediaMetricsDeathNotifier;
-
- public:
-
- enum Type {
- kTypeNone = 0,
- kTypeInt32 = 1,
- kTypeInt64 = 2,
- kTypeDouble = 3,
- kTypeCString = 4,
- kTypeRate = 5,
- };
-
- // sessionid
- // unique within device, within boot,
- typedef int64_t SessionID_t;
- static constexpr SessionID_t SessionIDInvalid = -1;
- static constexpr SessionID_t SessionIDNone = 0;
-
- // Key: the record descriminator
- // values for the record discriminator
- // values can be "component/component"
- // basic values: "video", "audio", "drm"
- // XXX: need to better define the format
- typedef std::string Key;
- static const Key kKeyNone; // ""
- static const Key kKeyAny; // "*"
-
- // Attr: names for attributes within a record
- // format "prop1" or "prop/subprop"
- // XXX: need to better define the format
- typedef const char *Attr;
-
-
- enum {
- PROTO_V0 = 0,
- PROTO_FIRST = PROTO_V0,
- PROTO_V1 = 1,
- PROTO_LAST = PROTO_V1,
- };
-
- private:
- // use the ::create() method instead
- MediaAnalyticsItem();
- MediaAnalyticsItem(Key);
- MediaAnalyticsItem(const MediaAnalyticsItem&);
- MediaAnalyticsItem &operator=(const MediaAnalyticsItem&);
-
- public:
-
- static MediaAnalyticsItem* create(Key key);
- static MediaAnalyticsItem* create();
-
- static MediaAnalyticsItem* convert(mediametrics_handle_t);
- static mediametrics_handle_t convert(MediaAnalyticsItem *);
-
- // access functions for the class
- ~MediaAnalyticsItem();
-
- // SessionID ties multiple submissions for same key together
- // so that if video "height" and "width" are known at one point
- // and "framerate" is only known later, they can be be brought
- // together.
- MediaAnalyticsItem &setSessionID(SessionID_t);
- MediaAnalyticsItem &clearSessionID();
- SessionID_t getSessionID() const;
- // generates and stores a new ID iff mSessionID == SessionIDNone
- SessionID_t generateSessionID();
-
- // reset all contents, discarding any extra data
- void clear();
- MediaAnalyticsItem *dup();
-
- // set the key discriminator for the record.
- // most often initialized as part of the constructor
- MediaAnalyticsItem &setKey(MediaAnalyticsItem::Key);
- MediaAnalyticsItem::Key getKey();
-
- // # of attributes in the record
- int32_t count() const;
-
- // set values appropriately
- void setInt32(Attr, int32_t value);
- void setInt64(Attr, int64_t value);
- void setDouble(Attr, double value);
- void setRate(Attr, int64_t count, int64_t duration);
- void setCString(Attr, const char *value);
-
- // fused get/add/set; if attr wasn't there, it's a simple set.
- // type-mismatch counts as "wasn't there".
- void addInt32(Attr, int32_t value);
- void addInt64(Attr, int64_t value);
- void addDouble(Attr, double value);
- void addRate(Attr, int64_t count, int64_t duration);
-
- // find & extract values
- // return indicates whether attr exists (and thus value filled in)
- // NULL parameter value suppresses storage of value.
- bool getInt32(Attr, int32_t *value);
- bool getInt64(Attr, int64_t *value);
- bool getDouble(Attr, double *value);
- bool getRate(Attr, int64_t *count, int64_t *duration, double *rate);
- // Caller owns the returned string
- bool getCString(Attr, char **value);
- bool getString(Attr, std::string *value);
-
- // parameter indicates whether to close any existing open
- // record with same key before establishing a new record
- // caller retains ownership of 'this'.
- bool selfrecord(bool);
- 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'
- int32_t filter(int count, Attr attrs[]);
- int32_t filterNot(int count, Attr attrs[]);
- int32_t filter(Attr 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
- int32_t writeToParcel(Parcel *);
- int32_t readFromParcel(const Parcel&);
-
- // supports the stable interface
- bool dumpAttributes(char **pbuffer, size_t *plength);
-
- std::string toString();
- std::string toString(int version);
- const char *toCString();
- const char *toCString(int version);
-
- // are we collecting analytics data
- static bool isEnabled();
-
- private:
- // handle Parcel version 0
- int32_t writeToParcel0(Parcel *);
- int32_t readFromParcel0(const Parcel&);
-
- 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);
-
- // enabled 1, disabled 0
- static const char * const EnabledProperty;
- static const char * const EnabledPropertyPersist;
- static const int EnabledProperty_default;
-
- private:
-
- // to help validate that A doesn't mess with B's records
- pid_t mPid;
- uid_t mUid;
- std::string mPkgName;
- int64_t mPkgVersionCode;
-
- // let's reuse a binder connection
- static sp<IMediaAnalyticsService> sAnalyticsService;
- static sp<IMediaAnalyticsService> getInstance();
- static void dropInstance();
-
- // tracking information
- SessionID_t mSessionID; // grouping similar records
- nsecs_t mTimestamp; // ns, system_time_monotonic
-
- // will this record accept further updates
- bool mFinalized;
-
- Key mKey;
-
- struct Prop {
-
- Type mType;
- const char *mName;
- size_t mNameLen; // the strlen(), doesn't include the null
- union {
- int32_t int32Value;
- int64_t int64Value;
- double doubleValue;
- char *CStringValue;
- struct { int64_t count, duration; } rate;
- } u;
- void setName(const char *name, size_t len);
- };
-
- void initProp(Prop *item);
- void clearProp(Prop *item);
- void clearPropValue(Prop *item);
- void copyProp(Prop *dst, const Prop *src);
- enum {
- kGrowProps = 10
- };
- bool growProps(int increment = kGrowProps);
- size_t findPropIndex(const char *name, size_t len);
- Prop *findProp(const char *name);
- Prop *allocateProp(const char *name);
- bool removeProp(const char *name);
-
- size_t mPropCount;
- size_t mPropSize;
- Prop *mProps;
-};
-
-} // namespace android
-
-#endif
diff --git a/media/libmediametrics/include/MediaMetricsItem.h b/media/libmediametrics/include/MediaMetricsItem.h
new file mode 100644
index 0000000..536c0e1
--- /dev/null
+++ b/media/libmediametrics/include/MediaMetricsItem.h
@@ -0,0 +1,1112 @@
+/*
+ * 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 <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,
+};
+
+/**
+ * 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 = 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 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());
+ }
+
+ 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 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;
+ std::string toString(int version) const;
+ const char *toCString();
+ const char *toCString(int version);
+
+ /**
+ * 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 5301f5c..762ed19 100644
--- a/media/libmediaplayerservice/Android.bp
+++ b/media/libmediaplayerservice/Android.bp
@@ -39,6 +39,7 @@
"libpowermanager",
"libstagefright",
"libstagefright_foundation",
+ "libstagefright_framecapture_utils",
"libstagefright_httplive",
"libutils",
],
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 46c130f..f2a38dd 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -58,7 +58,9 @@
#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>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/Utils.h>
@@ -265,6 +267,172 @@
return ok;
}
+static void dumpCodecDetails(int fd, const sp<IMediaCodecList> &codecList, bool queryDecoders) {
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ const char *codecType = queryDecoders? "Decoder" : "Encoder";
+ snprintf(buffer, SIZE - 1, "\n%s infos by media types:\n"
+ "=============================\n", codecType);
+ result.append(buffer);
+
+ size_t numCodecs = codecList->countCodecs();
+
+ // gather all media types supported by codec class, and link to codecs that support them
+ KeyedVector<AString, Vector<sp<MediaCodecInfo>>> allMediaTypes;
+ for (size_t codec_ix = 0; codec_ix < numCodecs; ++codec_ix) {
+ sp<MediaCodecInfo> info = codecList->getCodecInfo(codec_ix);
+ if (info->isEncoder() == !queryDecoders) {
+ Vector<AString> supportedMediaTypes;
+ info->getSupportedMediaTypes(&supportedMediaTypes);
+ if (!supportedMediaTypes.size()) {
+ snprintf(buffer, SIZE - 1, "warning: %s does not support any media types\n",
+ info->getCodecName());
+ result.append(buffer);
+ } else {
+ for (const AString &mediaType : supportedMediaTypes) {
+ if (allMediaTypes.indexOfKey(mediaType) < 0) {
+ allMediaTypes.add(mediaType, Vector<sp<MediaCodecInfo>>());
+ }
+ allMediaTypes.editValueFor(mediaType).add(info);
+ }
+ }
+ }
+ }
+
+ KeyedVector<AString, bool> visitedCodecs;
+ for (size_t type_ix = 0; type_ix < allMediaTypes.size(); ++type_ix) {
+ const AString &mediaType = allMediaTypes.keyAt(type_ix);
+ snprintf(buffer, SIZE - 1, "\nMedia type '%s':\n", mediaType.c_str());
+ result.append(buffer);
+
+ for (const sp<MediaCodecInfo> &info : allMediaTypes.valueAt(type_ix)) {
+ sp<MediaCodecInfo::Capabilities> caps = info->getCapabilitiesFor(mediaType.c_str());
+ if (caps == NULL) {
+ snprintf(buffer, SIZE - 1, "warning: %s does not have capabilities for type %s\n",
+ info->getCodecName(), mediaType.c_str());
+ result.append(buffer);
+ continue;
+ }
+ snprintf(buffer, SIZE - 1, " %s \"%s\" supports\n",
+ codecType, info->getCodecName());
+ result.append(buffer);
+
+ auto printList = [&](const char *type, const Vector<AString> &values){
+ snprintf(buffer, SIZE - 1, " %s: [", type);
+ result.append(buffer);
+ for (size_t j = 0; j < values.size(); ++j) {
+ snprintf(buffer, SIZE - 1, "\n %s%s", values[j].c_str(),
+ j == values.size() - 1 ? " " : ",");
+ result.append(buffer);
+ }
+ result.append("]\n");
+ };
+
+ if (visitedCodecs.indexOfKey(info->getCodecName()) < 0) {
+ visitedCodecs.add(info->getCodecName(), true);
+ {
+ Vector<AString> aliases;
+ info->getAliases(&aliases);
+ // quote alias
+ for (AString &alias : aliases) {
+ alias.insert("\"", 1, 0);
+ alias.append('"');
+ }
+ printList("aliases", aliases);
+ }
+ {
+ uint32_t attrs = info->getAttributes();
+ Vector<AString> list;
+ list.add(AStringPrintf("encoder: %d",
+ !!(attrs & MediaCodecInfo::kFlagIsEncoder)));
+ list.add(AStringPrintf("vendor: %d",
+ !!(attrs & MediaCodecInfo::kFlagIsVendor)));
+ list.add(AStringPrintf("software-only: %d",
+ !!(attrs & MediaCodecInfo::kFlagIsSoftwareOnly)));
+ list.add(AStringPrintf("hw-accelerated: %d",
+ !!(attrs & MediaCodecInfo::kFlagIsHardwareAccelerated)));
+ printList(AStringPrintf("attributes: %#x", attrs).c_str(), list);
+ }
+
+ snprintf(buffer, SIZE - 1, " owner: \"%s\"\n", info->getOwnerName());
+ result.append(buffer);
+ snprintf(buffer, SIZE - 1, " rank: %u\n", info->getRank());
+ result.append(buffer);
+ } else {
+ result.append(" aliases, attributes, owner, rank: see above\n");
+ }
+
+ {
+ Vector<AString> list;
+ Vector<MediaCodecInfo::ProfileLevel> profileLevels;
+ caps->getSupportedProfileLevels(&profileLevels);
+ for (const MediaCodecInfo::ProfileLevel &pl : profileLevels) {
+ const char *niceProfile =
+ mediaType.equalsIgnoreCase(MIMETYPE_AUDIO_AAC)
+ ? asString_AACObject(pl.mProfile) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_MPEG2)
+ ? asString_MPEG2Profile(pl.mProfile) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_H263)
+ ? asString_H263Profile(pl.mProfile) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_MPEG4)
+ ? asString_MPEG4Profile(pl.mProfile) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_AVC)
+ ? asString_AVCProfile(pl.mProfile) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_VP8)
+ ? asString_VP8Profile(pl.mProfile) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_HEVC)
+ ? asString_HEVCProfile(pl.mProfile) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_VP9)
+ ? asString_VP9Profile(pl.mProfile) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_AV1)
+ ? asString_AV1Profile(pl.mProfile) : "??";
+ const char *niceLevel =
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_MPEG2)
+ ? asString_MPEG2Level(pl.mLevel) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_H263)
+ ? asString_H263Level(pl.mLevel) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_MPEG4)
+ ? asString_MPEG4Level(pl.mLevel) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_AVC)
+ ? asString_AVCLevel(pl.mLevel) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_VP8)
+ ? asString_VP8Level(pl.mLevel) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_HEVC)
+ ? asString_HEVCTierLevel(pl.mLevel) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_VP9)
+ ? asString_VP9Level(pl.mLevel) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_AV1)
+ ? asString_AV1Level(pl.mLevel) : "??";
+
+ list.add(AStringPrintf("% 5u/% 5u (%s/%s)",
+ pl.mProfile, pl.mLevel, niceProfile, niceLevel));
+ }
+ printList("profile/levels", list);
+ }
+
+ {
+ Vector<AString> list;
+ Vector<uint32_t> colors;
+ caps->getSupportedColorFormats(&colors);
+ for (uint32_t color : colors) {
+ list.add(AStringPrintf("%#x (%s)", color,
+ asString_ColorFormat((int32_t)color)));
+ }
+ printList("colors", list);
+ }
+
+ result.append(" details: ");
+ result.append(caps->getDetails()->debugString(6).c_str());
+ result.append("\n");
+ }
+ }
+ result.append("\n");
+ ::write(fd, result.string(), result.size());
+}
+
+
// TODO: Find real cause of Audio/Video delay in PV framework and remove this workaround
/* static */ int MediaPlayerService::AudioOutput::mMinBufferCount = 4;
/* static */ bool MediaPlayerService::AudioOutput::mIsOnEmulator = false;
@@ -280,6 +448,8 @@
mNextConnId = 1;
MediaPlayerFactory::registerBuiltinFactories();
+ // initialize the frame capture utilities
+ (void)FrameCaptureProcessor::getInstance();
}
MediaPlayerService::~MediaPlayerService()
@@ -424,7 +594,7 @@
SortedVector< sp<MediaRecorderClient> > mediaRecorderClients;
if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
- snprintf(buffer, SIZE, "Permission Denial: "
+ snprintf(buffer, SIZE - 1, "Permission Denial: "
"can't dump MediaPlayerService from pid=%d, uid=%d\n",
IPCThreadState::self()->getCallingPid(),
IPCThreadState::self()->getCallingUid());
@@ -453,11 +623,11 @@
}
result.append(" Files opened and/or mapped:\n");
- snprintf(buffer, SIZE, "/proc/%d/maps", getpid());
+ snprintf(buffer, SIZE - 1, "/proc/%d/maps", getpid());
FILE *f = fopen(buffer, "r");
if (f) {
while (!feof(f)) {
- fgets(buffer, SIZE, f);
+ fgets(buffer, SIZE - 1, f);
if (strstr(buffer, " /storage/") ||
strstr(buffer, " /system/sounds/") ||
strstr(buffer, " /data/") ||
@@ -473,13 +643,13 @@
result.append("\n");
}
- snprintf(buffer, SIZE, "/proc/%d/fd", getpid());
+ snprintf(buffer, SIZE - 1, "/proc/%d/fd", getpid());
DIR *d = opendir(buffer);
if (d) {
struct dirent *ent;
while((ent = readdir(d)) != NULL) {
if (strcmp(ent->d_name,".") && strcmp(ent->d_name,"..")) {
- snprintf(buffer, SIZE, "/proc/%d/fd/%s", getpid(), ent->d_name);
+ snprintf(buffer, SIZE - 1, "/proc/%d/fd/%s", getpid(), ent->d_name);
struct stat s;
if (lstat(buffer, &s) == 0) {
if ((s.st_mode & S_IFMT) == S_IFLNK) {
@@ -522,6 +692,10 @@
gLooperRoster.dump(fd, args);
+ sp<IMediaCodecList> codecList = getCodecList();
+ dumpCodecDetails(fd, codecList, true /* decoders */);
+ dumpCodecDetails(fd, codecList, false /* !decoders */);
+
bool dumpMem = false;
bool unreachableMemory = false;
for (size_t i = 0; i < args.size(); i++) {
@@ -544,6 +718,7 @@
}
}
write(fd, result.string(), result.size());
+
return NO_ERROR;
}
diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp
index 703da4b..9b1974b 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.cpp
+++ b/media/libmediaplayerservice/MediaRecorderClient.cpp
@@ -32,6 +32,7 @@
#include <cutils/atomic.h>
#include <cutils/properties.h> // for property_get
#include <gui/IGraphicBufferProducer.h>
+#include <mediautils/ServiceUtilities.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <system/audio.h>
@@ -44,7 +45,6 @@
namespace android {
const char* cameraPermission = "android.permission.CAMERA";
-const char* recordAudioPermission = "android.permission.RECORD_AUDIO";
static bool checkPermission(const char* permissionString) {
if (getpid() == IPCThreadState::self()->getCallingPid()) return true;
@@ -118,7 +118,16 @@
status_t MediaRecorderClient::setAudioSource(int as)
{
ALOGV("setAudioSource(%d)", as);
- if (!checkPermission(recordAudioPermission)) {
+ if (as < AUDIO_SOURCE_DEFAULT
+ || (as >= AUDIO_SOURCE_CNT && as != AUDIO_SOURCE_FM_TUNER)) {
+ ALOGE("Invalid audio source: %d", as);
+ return BAD_VALUE;
+ }
+ pid_t pid = IPCThreadState::self()->getCallingPid();
+ uid_t uid = IPCThreadState::self()->getCallingUid();
+
+ if ((as == AUDIO_SOURCE_FM_TUNER && !captureAudioOutputAllowed(pid, uid))
+ || !recordingAllowed(String16(""), pid, uid)) {
return PERMISSION_DENIED;
}
Mutex::Autolock lock(mLock);
@@ -129,6 +138,29 @@
return mRecorder->setAudioSource((audio_source_t)as);
}
+status_t MediaRecorderClient::setPrivacySensitive(bool privacySensitive)
+{
+ ALOGV("%s(%s)", __func__, privacySensitive ? "true" : "false");
+ Mutex::Autolock lock(mLock);
+ if (mRecorder == NULL) {
+ ALOGE("%s: recorder is not initialized", __func__);
+ return NO_INIT;
+ }
+ return mRecorder->setPrivacySensitive(privacySensitive);
+}
+
+status_t MediaRecorderClient::isPrivacySensitive(bool *privacySensitive) const
+{
+ Mutex::Autolock lock(mLock);
+ if (mRecorder == NULL) {
+ ALOGE("%s: recorder is not initialized", __func__);
+ return NO_INIT;
+ }
+ status_t status = mRecorder->isPrivacySensitive(privacySensitive);
+ ALOGV("%s: status: %d enabled: %s", __func__, status, *privacySensitive ? "true" : "false");
+ return status;
+}
+
status_t MediaRecorderClient::setOutputFormat(int of)
{
ALOGV("setOutputFormat(%d)", of);
diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h
index 9e0f877..12257e5 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.h
+++ b/media/libmediaplayerservice/MediaRecorderClient.h
@@ -51,6 +51,8 @@
virtual status_t setPreviewSurface(const sp<IGraphicBufferProducer>& surface);
virtual status_t setVideoSource(int vs);
virtual status_t setAudioSource(int as);
+ status_t setPrivacySensitive(bool privacySensitive) override;
+ status_t isPrivacySensitive(bool *privacySensitive) const override;
virtual status_t setOutputFormat(int of);
virtual status_t setVideoEncoder(int ve);
virtual status_t setAudioEncoder(int ae);
@@ -98,7 +100,7 @@
sp<AudioDeviceUpdatedNotifier> mAudioDeviceUpdatedNotifier;
pid_t mPid;
- Mutex mLock;
+ mutable Mutex mLock;
MediaRecorderBase *mRecorder;
sp<MediaPlayerService> mMediaPlayerService;
};
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
index 4dbab0a..fb228ca 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
@@ -243,31 +243,27 @@
sp<IMemory> frame = mRetriever->getImageRectAtIndex(
index, colorFormat, left, top, right, bottom);
if (frame == NULL) {
- ALOGE("failed to extract image");
- return NULL;
+ ALOGE("failed to extract image at index %d", index);
}
return frame;
}
-status_t MetadataRetrieverClient::getFrameAtIndex(
- std::vector<sp<IMemory> > *frames,
- int frameIndex, int numFrames, int colorFormat, bool metaOnly) {
- ALOGV("getFrameAtIndex: frameIndex(%d), numFrames(%d), colorFormat(%d), metaOnly(%d)",
- frameIndex, numFrames, colorFormat, metaOnly);
+sp<IMemory> MetadataRetrieverClient::getFrameAtIndex(
+ int index, int colorFormat, bool metaOnly) {
+ ALOGV("getFrameAtIndex: index(%d), colorFormat(%d), metaOnly(%d)",
+ index, colorFormat, metaOnly);
Mutex::Autolock lock(mLock);
Mutex::Autolock glock(sLock);
if (mRetriever == NULL) {
ALOGE("retriever is not initialized");
- return INVALID_OPERATION;
+ return NULL;
}
- status_t err = mRetriever->getFrameAtIndex(
- frames, frameIndex, numFrames, colorFormat, metaOnly);
- if (err != OK) {
- frames->clear();
- return err;
+ sp<IMemory> frame = mRetriever->getFrameAtIndex(index, colorFormat, metaOnly);
+ if (frame == NULL) {
+ ALOGE("failed to extract frame at index %d", index);
}
- return OK;
+ return frame;
}
sp<IMemory> MetadataRetrieverClient::extractAlbumArt()
@@ -297,7 +293,7 @@
delete albumArt;
return NULL;
}
- MediaAlbumArt::init((MediaAlbumArt *) mAlbumArt->pointer(),
+ MediaAlbumArt::init((MediaAlbumArt *) mAlbumArt->unsecurePointer(),
albumArt->size(), albumArt->data());
delete albumArt; // We've taken our copy.
return mAlbumArt;
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.h b/media/libmediaplayerservice/MetadataRetrieverClient.h
index 272d093..8020441 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.h
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.h
@@ -56,9 +56,8 @@
int index, int colorFormat, bool metaOnly, bool thumbnail);
virtual sp<IMemory> getImageRectAtIndex(
int index, int colorFormat, int left, int top, int right, int bottom);
- virtual status_t getFrameAtIndex(
- std::vector<sp<IMemory> > *frames,
- int frameIndex, int numFrames, int colorFormat, bool metaOnly);
+ virtual sp<IMemory> getFrameAtIndex(
+ int index, int colorFormat, bool metaOnly);
virtual sp<IMemory> extractAlbumArt();
virtual const char* extractMetadata(int keyCode);
diff --git a/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp b/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp
index 1aae241..41b6f72 100644
--- a/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp
+++ b/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp
@@ -44,7 +44,7 @@
StagefrightMetadataRetriever::StagefrightMetadataRetriever()
: mParsedMetaData(false),
mAlbumArt(NULL),
- mLastImageIndex(-1) {
+ mLastDecodedIndex(-1) {
ALOGV("StagefrightMetadataRetriever()");
}
@@ -144,8 +144,8 @@
FrameRect rect = {left, top, right, bottom};
- if (mImageDecoder != NULL && index == mLastImageIndex) {
- return mImageDecoder->extractFrame(&rect);
+ if (mDecoder != NULL && index == mLastDecodedIndex) {
+ return mDecoder->extractFrame(&rect);
}
return getImageInternal(
@@ -154,6 +154,8 @@
sp<IMemory> StagefrightMetadataRetriever::getImageInternal(
int index, int colorFormat, bool metaOnly, bool thumbnail, FrameRect* rect) {
+ mDecoder.clear();
+ mLastDecodedIndex = -1;
if (mExtractor.get() == NULL) {
ALOGE("no extractor.");
@@ -228,14 +230,14 @@
const AString &componentName = matchingCodecs[i];
sp<ImageDecoder> decoder = new ImageDecoder(componentName, trackMeta, source);
int64_t frameTimeUs = thumbnail ? -1 : 0;
- if (decoder->init(frameTimeUs, 1 /*numFrames*/, 0 /*option*/, colorFormat) == OK) {
+ if (decoder->init(frameTimeUs, 0 /*option*/, colorFormat) == OK) {
sp<IMemory> frame = decoder->extractFrame(rect);
if (frame != NULL) {
if (rect != NULL) {
// keep the decoder if slice decoding
- mImageDecoder = decoder;
- mLastImageIndex = index;
+ mDecoder = decoder;
+ mLastDecodedIndex = index;
}
return frame;
}
@@ -243,6 +245,7 @@
ALOGV("%s failed to extract thumbnail, trying next decoder.", componentName.c_str());
}
+ ALOGE("all codecs failed to extract frame.");
return NULL;
}
@@ -251,36 +254,40 @@
ALOGV("getFrameAtTime: %" PRId64 " us option: %d colorFormat: %d, metaOnly: %d",
timeUs, option, colorFormat, metaOnly);
- sp<IMemory> frame;
- status_t err = getFrameInternal(
- timeUs, 1, option, colorFormat, metaOnly, &frame, NULL /*outFrames*/);
- return (err == OK) ? frame : NULL;
+ return getFrameInternal(timeUs, option, colorFormat, metaOnly);
}
-status_t StagefrightMetadataRetriever::getFrameAtIndex(
- std::vector<sp<IMemory> >* frames,
- int frameIndex, int numFrames, int colorFormat, bool metaOnly) {
- ALOGV("getFrameAtIndex: frameIndex %d, numFrames %d, colorFormat: %d, metaOnly: %d",
- frameIndex, numFrames, colorFormat, metaOnly);
+sp<IMemory> StagefrightMetadataRetriever::getFrameAtIndex(
+ int frameIndex, int colorFormat, bool metaOnly) {
+ ALOGV("getFrameAtIndex: frameIndex %d, colorFormat: %d, metaOnly: %d",
+ frameIndex, colorFormat, metaOnly);
+ if (mDecoder != NULL && frameIndex == mLastDecodedIndex + 1) {
+ sp<IMemory> frame = mDecoder->extractFrame();
+ if (frame != nullptr) {
+ mLastDecodedIndex = frameIndex;
+ }
+ return frame;
+ }
- return getFrameInternal(
- frameIndex, numFrames, MediaSource::ReadOptions::SEEK_FRAME_INDEX,
- colorFormat, metaOnly, NULL /*outFrame*/, frames);
+ return getFrameInternal(frameIndex,
+ MediaSource::ReadOptions::SEEK_FRAME_INDEX, colorFormat, metaOnly);
}
-status_t StagefrightMetadataRetriever::getFrameInternal(
- int64_t timeUs, int numFrames, int option, int colorFormat, bool metaOnly,
- sp<IMemory>* outFrame, std::vector<sp<IMemory> >* outFrames) {
+sp<IMemory> StagefrightMetadataRetriever::getFrameInternal(
+ int64_t timeUs, int option, int colorFormat, bool metaOnly) {
+ mDecoder.clear();
+ mLastDecodedIndex = -1;
+
if (mExtractor.get() == NULL) {
ALOGE("no extractor.");
- return NO_INIT;
+ return NULL;
}
sp<MetaData> fileMeta = mExtractor->getMetaData();
if (fileMeta == NULL) {
ALOGE("extractor doesn't publish metadata, failed to initialize?");
- return NO_INIT;
+ return NULL;
}
size_t n = mExtractor->countTracks();
@@ -301,30 +308,24 @@
if (i == n) {
ALOGE("no video track found.");
- return INVALID_OPERATION;
+ return NULL;
}
sp<MetaData> trackMeta = mExtractor->getTrackMetaData(
i, MediaExtractor::kIncludeExtensiveMetaData);
if (!trackMeta) {
- return UNKNOWN_ERROR;
+ return NULL;
}
if (metaOnly) {
- if (outFrame != NULL) {
- *outFrame = FrameDecoder::getMetadataOnly(trackMeta, colorFormat);
- if (*outFrame != NULL) {
- return OK;
- }
- }
- return UNKNOWN_ERROR;
+ return FrameDecoder::getMetadataOnly(trackMeta, colorFormat);
}
sp<IMediaSource> source = mExtractor->getTrack(i);
if (source.get() == NULL) {
ALOGV("unable to instantiate video track.");
- return UNKNOWN_ERROR;
+ return NULL;
}
const void *data;
@@ -351,24 +352,22 @@
for (size_t i = 0; i < matchingCodecs.size(); ++i) {
const AString &componentName = matchingCodecs[i];
sp<VideoFrameDecoder> decoder = new VideoFrameDecoder(componentName, trackMeta, source);
- if (decoder->init(timeUs, numFrames, option, colorFormat) == OK) {
- if (outFrame != NULL) {
- *outFrame = decoder->extractFrame();
- if (*outFrame != NULL) {
- return OK;
+ if (decoder->init(timeUs, option, colorFormat) == OK) {
+ sp<IMemory> frame = decoder->extractFrame();
+ if (frame != nullptr) {
+ // keep the decoder if seeking by frame index
+ if (option == MediaSource::ReadOptions::SEEK_FRAME_INDEX) {
+ mDecoder = decoder;
+ mLastDecodedIndex = timeUs;
}
- } else if (outFrames != NULL) {
- status_t err = decoder->extractFrames(outFrames);
- if (err == OK) {
- return OK;
- }
+ return frame;
}
}
ALOGV("%s failed to extract frame, trying next decoder.", componentName.c_str());
}
ALOGE("all codecs failed to extract frame.");
- return UNKNOWN_ERROR;
+ return NULL;
}
MediaAlbumArt *StagefrightMetadataRetriever::extractAlbumArt() {
diff --git a/media/libmediaplayerservice/StagefrightMetadataRetriever.h b/media/libmediaplayerservice/StagefrightMetadataRetriever.h
index c50677a..c09a501 100644
--- a/media/libmediaplayerservice/StagefrightMetadataRetriever.h
+++ b/media/libmediaplayerservice/StagefrightMetadataRetriever.h
@@ -18,7 +18,7 @@
#define STAGEFRIGHT_METADATA_RETRIEVER_H_
-#include <media/IMediaExtractor.h>
+#include <android/IMediaExtractor.h>
#include <media/MediaMetadataRetrieverInterface.h>
#include <utils/KeyedVector.h>
@@ -26,7 +26,7 @@
namespace android {
class DataSource;
-struct ImageDecoder;
+struct FrameDecoder;
struct FrameRect;
struct StagefrightMetadataRetriever : public MediaMetadataRetrieverBase {
@@ -47,9 +47,8 @@
int index, int colorFormat, bool metaOnly, bool thumbnail);
virtual sp<IMemory> getImageRectAtIndex(
int index, int colorFormat, int left, int top, int right, int bottom);
- virtual status_t getFrameAtIndex(
- std::vector<sp<IMemory> >* frames,
- int frameIndex, int numFrames, int colorFormat, bool metaOnly);
+ virtual sp<IMemory> getFrameAtIndex(
+ int index, int colorFormat, bool metaOnly);
virtual MediaAlbumArt *extractAlbumArt();
virtual const char *extractMetadata(int keyCode);
@@ -62,17 +61,17 @@
KeyedVector<int, String8> mMetaData;
MediaAlbumArt *mAlbumArt;
- sp<ImageDecoder> mImageDecoder;
- int mLastImageIndex;
+ sp<FrameDecoder> mDecoder;
+ int mLastDecodedIndex;
void parseMetaData();
void parseColorAspects(const sp<MetaData>& meta);
// Delete album art and clear metadata.
void clearMetadata();
- status_t getFrameInternal(
- int64_t timeUs, int numFrames, int option, int colorFormat, bool metaOnly,
- sp<IMemory>* outFrame, std::vector<sp<IMemory> >* outFrames);
- virtual sp<IMemory> getImageInternal(
+ sp<IMemory> getFrameInternal(
+ int64_t timeUs, int option, int colorFormat, bool metaOnly);
+
+ sp<IMemory> getImageInternal(
int index, int colorFormat, bool metaOnly, bool thumbnail, FrameRect* rect);
StagefrightMetadataRetriever(const StagefrightMetadataRetriever &);
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 63681fa..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>
@@ -115,6 +115,7 @@
mWriter(NULL),
mOutputFd(-1),
mAudioSource((audio_source_t)AUDIO_SOURCE_CNT), // initialize with invalid value
+ mPrivacySensitive(PRIVACY_SENSITIVE_DEFAULT),
mVideoSource(VIDEO_SOURCE_LIST_END),
mStarted(false),
mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),
@@ -137,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);
}
@@ -146,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);
}
}
@@ -232,18 +233,37 @@
status_t StagefrightRecorder::setAudioSource(audio_source_t as) {
ALOGV("setAudioSource: %d", as);
- if (as < AUDIO_SOURCE_DEFAULT ||
- (as >= AUDIO_SOURCE_CNT && as != AUDIO_SOURCE_FM_TUNER)) {
- ALOGE("Invalid audio source: %d", as);
- return BAD_VALUE;
- }
if (as == AUDIO_SOURCE_DEFAULT) {
mAudioSource = AUDIO_SOURCE_MIC;
} else {
mAudioSource = as;
}
+ // Reset privacy sensitive in case this is the second time audio source is set
+ mPrivacySensitive = PRIVACY_SENSITIVE_DEFAULT;
+ return OK;
+}
+status_t StagefrightRecorder::setPrivacySensitive(bool privacySensitive) {
+ // privacy sensitive cannot be set before audio source is set
+ if (mAudioSource == AUDIO_SOURCE_CNT) {
+ return INVALID_OPERATION;
+ }
+ mPrivacySensitive = privacySensitive ? PRIVACY_SENSITIVE_ENABLED : PRIVACY_SENSITIVE_DISABLED;
+ return OK;
+}
+
+status_t StagefrightRecorder::isPrivacySensitive(bool *privacySensitive) const {
+ *privacySensitive = false;
+ if (mAudioSource == AUDIO_SOURCE_CNT) {
+ return INVALID_OPERATION;
+ }
+ if (mPrivacySensitive == PRIVACY_SENSITIVE_DEFAULT) {
+ *privacySensitive = mAudioSource == AUDIO_SOURCE_VOICE_COMMUNICATION
+ || mAudioSource == AUDIO_SOURCE_CAMCORDER;
+ } else {
+ *privacySensitive = mPrivacySensitive == PRIVACY_SENSITIVE_ENABLED;
+ }
return OK;
}
@@ -1087,9 +1107,35 @@
}
}
+ audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
+ attr.source = mAudioSource;
+ // attr.flags AUDIO_FLAG_CAPTURE_PRIVATE is cleared by default
+ if (mPrivacySensitive == PRIVACY_SENSITIVE_DEFAULT) {
+ if (attr.source == AUDIO_SOURCE_VOICE_COMMUNICATION
+ || attr.source == AUDIO_SOURCE_CAMCORDER) {
+ attr.flags |= AUDIO_FLAG_CAPTURE_PRIVATE;
+ mPrivacySensitive = PRIVACY_SENSITIVE_ENABLED;
+ } else {
+ mPrivacySensitive = PRIVACY_SENSITIVE_DISABLED;
+ }
+ } else {
+ if (mAudioSource == AUDIO_SOURCE_REMOTE_SUBMIX
+ || mAudioSource == AUDIO_SOURCE_FM_TUNER
+ || mAudioSource == AUDIO_SOURCE_VOICE_DOWNLINK
+ || mAudioSource == AUDIO_SOURCE_VOICE_UPLINK
+ || mAudioSource == AUDIO_SOURCE_VOICE_CALL
+ || mAudioSource == AUDIO_SOURCE_ECHO_REFERENCE) {
+ ALOGE("Cannot request private capture with source: %d", mAudioSource);
+ return NULL;
+ }
+ if (mPrivacySensitive == PRIVACY_SENSITIVE_ENABLED) {
+ attr.flags |= AUDIO_FLAG_CAPTURE_PRIVATE;
+ }
+ }
+
sp<AudioSource> audioSource =
new AudioSource(
- mAudioSource,
+ &attr,
mOpPackageName,
sourceSampleRate,
mAudioChannels,
@@ -1138,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());
}
}
@@ -1699,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());
}
}
@@ -1956,7 +2002,6 @@
(*meta)->setInt32(kKeyTimeScale, mMovieTimeScale);
}
if (mOutputFormat != OUTPUT_FORMAT_WEBM) {
- (*meta)->setInt32(kKey64BitFileOffset, mUse64BitFileOffset);
if (mTrackEveryTimeDurationUs > 0) {
(*meta)->setInt64(kKeyTrackTimeStatus, mTrackEveryTimeDurationUs);
}
@@ -2220,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 8bf083a..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>
@@ -46,6 +46,8 @@
virtual ~StagefrightRecorder();
virtual status_t init();
virtual status_t setAudioSource(audio_source_t as);
+ status_t setPrivacySensitive(bool privacySensitive) override;
+ status_t isPrivacySensitive(bool *privacySensitive) const override;
virtual status_t setVideoSource(video_source vs);
virtual status_t setOutputFormat(output_format of);
virtual status_t setAudioEncoder(audio_encoder ae);
@@ -82,6 +84,13 @@
status_t getPortId(audio_port_handle_t *portId) const override;
private:
+
+ enum privacy_sensitive_t {
+ PRIVACY_SENSITIVE_DEFAULT = -1,
+ PRIVACY_SENSITIVE_DISABLED = 0,
+ PRIVACY_SENSITIVE_ENABLED = 1,
+ };
+
mutable Mutex mLock;
sp<hardware::ICamera> mCamera;
sp<ICameraRecordingProxy> mCameraProxy;
@@ -95,12 +104,13 @@
int mOutputFd;
sp<AudioSource> mAudioSourceNode;
- MediaAnalyticsItem *mAnalyticsItem;
+ mediametrics::Item *mMetricsItem;
bool mAnalyticsDirty;
void flushAndResetMetrics(bool reinitialize);
void updateMetrics();
audio_source_t mAudioSource;
+ privacy_sensitive_t mPrivacySensitive;
video_source mVideoSource;
output_format mOutputFormat;
audio_encoder mAudioEncoder;
diff --git a/media/libmediaplayerservice/datasource/include/datasource/PlayerServiceMediaHTTP.h b/media/libmediaplayerservice/datasource/include/datasource/PlayerServiceMediaHTTP.h
index b5124dc..2f94ada 100644
--- a/media/libmediaplayerservice/datasource/include/datasource/PlayerServiceMediaHTTP.h
+++ b/media/libmediaplayerservice/datasource/include/datasource/PlayerServiceMediaHTTP.h
@@ -19,6 +19,7 @@
#define PLAYER_SERVICE_MEDIA_HTTP_H_
#include <datasource/MediaHTTP.h>
+#include <drm/DrmManagerClient.h>
#include <media/stagefright/foundation/AString.h>
namespace android {
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 00e3443..0eaa503 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -29,13 +29,14 @@
#include <datasource/NuCachedSource2.h>
#include <media/DataSource.h>
#include <media/MediaBufferHolder.h>
-#include <media/MediaSource.h>
-#include <media/IMediaExtractorService.h>
+#include <media/stagefright/MediaSource.h>
+#include <android/IMediaExtractorService.h>
#include <media/IMediaHTTPService.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/InterfaceUtils.h>
+#include <media/stagefright/FoundationUtils.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaClock.h>
#include <media/stagefright/MediaDefs.h>
@@ -75,7 +76,6 @@
mUIDValid(uidValid),
mUID(uid),
mMediaClock(mediaClock),
- mFd(-1),
mBitrate(-1LL),
mPendingReadBufferTypes(0) {
ALOGV("GenericSource");
@@ -98,10 +98,7 @@
mUri.clear();
mUriHeaders.clear();
mSources.clear();
- if (mFd >= 0) {
- close(mFd);
- mFd = -1;
- }
+ mFd.reset();
mOffset = 0;
mLength = 0;
mStarted = false;
@@ -137,11 +134,11 @@
status_t NuPlayer::GenericSource::setDataSource(
int fd, int64_t offset, int64_t length) {
Mutex::Autolock _l(mLock);
- ALOGV("setDataSource %d/%lld/%lld", fd, (long long)offset, (long long)length);
+ ALOGV("setDataSource %d/%lld/%lld (%s)", fd, (long long)offset, (long long)length, nameForFd(fd).c_str());
resetDataSource();
- mFd = dup(fd);
+ mFd.reset(dup(fd));
mOffset = offset;
mLength = length;
@@ -413,24 +410,19 @@
} else {
if (property_get_bool("media.stagefright.extractremote", true) &&
!PlayerServiceFileSource::requiresDrm(
- mFd, mOffset, mLength, nullptr /* mime */)) {
+ mFd.get(), mOffset, mLength, nullptr /* mime */)) {
sp<IBinder> binder =
defaultServiceManager()->getService(String16("media.extractor"));
if (binder != nullptr) {
ALOGD("FileSource remote");
sp<IMediaExtractorService> mediaExService(
interface_cast<IMediaExtractorService>(binder));
- sp<IDataSource> source =
- mediaExService->makeIDataSource(mFd, mOffset, mLength);
+ sp<IDataSource> source;
+ mediaExService->makeIDataSource(base::unique_fd(dup(mFd.get())), mOffset, mLength, &source);
ALOGV("IDataSource(FileSource): %p %d %lld %lld",
- source.get(), mFd, (long long)mOffset, (long long)mLength);
+ source.get(), mFd.get(), (long long)mOffset, (long long)mLength);
if (source.get() != nullptr) {
mDataSource = CreateDataSourceFromIDataSource(source);
- if (mDataSource != nullptr) {
- // Close the local file descriptor as it is not needed anymore.
- close(mFd);
- mFd = -1;
- }
} else {
ALOGW("extractor service cannot make data source");
}
@@ -440,12 +432,8 @@
}
if (mDataSource == nullptr) {
ALOGD("FileSource local");
- mDataSource = new PlayerServiceFileSource(mFd, mOffset, mLength);
+ mDataSource = new PlayerServiceFileSource(mFd.get(), mOffset, mLength);
}
- // TODO: close should always be done on mFd, see the lines following
- // CreateDataSourceFromIDataSource above,
- // and the FileSource constructor should dup the mFd argument as needed.
- mFd = -1;
}
if (mDataSource == NULL) {
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index 4d1905d..7a2ab8f 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -23,6 +23,7 @@
#include "ATSParser.h"
+#include <android-base/unique_fd.h>
#include <media/mediaplayer.h>
#include <media/stagefright/MediaBuffer.h>
@@ -154,7 +155,7 @@
sp<IMediaHTTPService> mHTTPService;
AString mUri;
KeyedVector<String8, String8> mUriHeaders;
- int mFd;
+ base::unique_fd mFd;
int64_t mOffset;
int64_t mLength;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index bd2b884..f734439 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -106,16 +106,17 @@
releaseAndResetMediaBuffers();
}
-sp<AMessage> NuPlayer::Decoder::getStats() const {
+sp<AMessage> NuPlayer::Decoder::getStats() {
+ Mutex::Autolock autolock(mStatsLock);
mStats->setInt64("frames-total", mNumFramesTotal);
mStats->setInt64("frames-dropped-input", mNumInputFramesDropped);
mStats->setInt64("frames-dropped-output", mNumOutputFramesDropped);
mStats->setFloat("frame-rate-total", mFrameRateTotal);
- // i'm mutexed right now.
// make our own copy, so we aren't victim to any later changes.
sp<AMessage> copiedStats = mStats->dup();
+
return copiedStats;
}
@@ -362,13 +363,17 @@
CHECK_EQ((status_t)OK, mCodec->getOutputFormat(&mOutputFormat));
CHECK_EQ((status_t)OK, mCodec->getInputFormat(&mInputFormat));
- mStats->setString("mime", mime.c_str());
- mStats->setString("component-name", mComponentName.c_str());
+ {
+ Mutex::Autolock autolock(mStatsLock);
+ mStats->setString("mime", mime.c_str());
+ mStats->setString("component-name", mComponentName.c_str());
+ }
if (!mIsAudio) {
int32_t width, height;
if (mOutputFormat->findInt32("width", &width)
&& mOutputFormat->findInt32("height", &height)) {
+ Mutex::Autolock autolock(mStatsLock);
mStats->setInt32("width", width);
mStats->setInt32("height", height);
}
@@ -799,6 +804,7 @@
int32_t width, height;
if (format->findInt32("width", &width)
&& format->findInt32("height", &height)) {
+ Mutex::Autolock autolock(mStatsLock);
mStats->setInt32("width", width);
mStats->setInt32("height", height);
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index 3da2f0b..4a52b0c 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -34,7 +34,7 @@
const sp<Surface> &surface = NULL,
const sp<CCDecoder> &ccDecoder = NULL);
- virtual sp<AMessage> getStats() const;
+ virtual sp<AMessage> getStats();
// sets the output surface of video decoders.
virtual status_t setVideoSurface(const sp<Surface> &surface);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
index d44c396..a3e0046 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
@@ -47,7 +47,7 @@
void signalResume(bool notifyComplete);
void initiateShutdown();
- virtual sp<AMessage> getStats() const {
+ virtual sp<AMessage> getStats() {
return mStats;
}
@@ -88,6 +88,7 @@
int32_t mBufferGeneration;
bool mPaused;
sp<AMessage> mStats;
+ Mutex mStatsLock;
private:
enum {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 95c973a..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 %d 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/NuPlayerDrm.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp
index 2d0c9e0..6788b56 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp
@@ -19,8 +19,7 @@
#include "NuPlayerDrm.h"
-#include <binder/IServiceManager.h>
-#include <mediadrm/IMediaDrmService.h>
+#include <mediadrm/DrmUtils.h>
#include <utils/Log.h>
@@ -30,60 +29,13 @@
sp<IDrm> NuPlayerDrm::CreateDrm(status_t *pstatus)
{
- status_t &status = *pstatus;
- sp<IServiceManager> sm = defaultServiceManager();
- sp<IBinder> binder = sm->getService(String16("media.drm"));
- ALOGV("CreateDrm binder %p", (binder != NULL ? binder.get() : 0));
-
- sp<IMediaDrmService> service = interface_cast<IMediaDrmService>(binder);
- if (service == NULL) {
- ALOGE("CreateDrm failed at IMediaDrmService");
- return NULL;
- }
-
- sp<IDrm> drm = service->makeDrm();
- if (drm == NULL) {
- ALOGE("CreateDrm failed at makeDrm");
- return NULL;
- }
-
- // this is before plugin creation so NO_INIT is fine
- status = drm->initCheck();
- if (status != OK && status != NO_INIT) {
- ALOGE("CreateDrm failed drm->initCheck(): %d", status);
- return NULL;
- }
- return drm;
+ return DrmUtils::MakeDrm(pstatus);
}
sp<ICrypto> NuPlayerDrm::createCrypto(status_t *pstatus)
{
- status_t &status = *pstatus;
- sp<IServiceManager> sm = defaultServiceManager();
- sp<IBinder> binder = sm->getService(String16("media.drm"));
- sp<IMediaDrmService> service = interface_cast<IMediaDrmService>(binder);
- if (service == NULL) {
- status = UNKNOWN_ERROR;
- ALOGE("CreateCrypto failed at IMediaDrmService");
- return NULL;
- }
-
- sp<ICrypto> crypto = service->makeCrypto();
- if (crypto == NULL) {
- status = UNKNOWN_ERROR;
- ALOGE("createCrypto failed");
- return NULL;
- }
-
- // this is before plugin creation so NO_INIT is fine
- status = crypto->initCheck();
- if (status != OK && status != NO_INIT) {
- ALOGE("createCrypto failed crypto->initCheck(): %d", status);
- return NULL;
- }
-
- return crypto;
+ return DrmUtils::MakeCrypto(pstatus);
}
Vector<DrmUUID> NuPlayerDrm::parsePSSH(const void *pssh, size_t psshsize)
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 39be40d..c30f048 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -33,6 +33,7 @@
#include <media/stagefright/Utils.h>
#include <media/stagefright/VideoFrameScheduler.h>
#include <media/MediaCodecBuffer.h>
+#include <utils/SystemClock.h>
#include <inttypes.h>
@@ -156,6 +157,7 @@
CHECK(mediaClock != NULL);
mPlaybackRate = mPlaybackSettings.mSpeed;
mMediaClock->setPlaybackRate(mPlaybackRate);
+ (void)mSyncFlag.test_and_set();
}
NuPlayer::Renderer::~Renderer() {
@@ -326,9 +328,27 @@
mSyncQueues = false;
}
+ // Wait until the current job in the message queue is done, to make sure
+ // buffer processing from the old generation is finished. After the current
+ // job is finished, access to buffers are protected by generation.
+ Mutex::Autolock syncLock(mSyncLock);
+ int64_t syncCount = mSyncCount;
+ mSyncFlag.clear();
+
+ // Make sure message queue is not empty after mSyncFlag is cleared.
sp<AMessage> msg = new AMessage(kWhatFlush, this);
msg->setInt32("audio", static_cast<int32_t>(audio));
msg->post();
+
+ int64_t uptimeMs = uptimeMillis();
+ while (mSyncCount == syncCount) {
+ (void)mSyncCondition.waitRelative(mSyncLock, ms2ns(1000));
+ if (uptimeMillis() - uptimeMs > 1000) {
+ ALOGW("flush(): no wake-up from sync point for 1s; stop waiting to "
+ "prevent being stuck indefinitely.");
+ break;
+ }
+ }
}
void NuPlayer::Renderer::signalTimeDiscontinuity() {
@@ -781,6 +801,11 @@
TRESPASS();
break;
}
+ if (!mSyncFlag.test_and_set()) {
+ Mutex::Autolock syncLock(mSyncLock);
+ ++mSyncCount;
+ mSyncCondition.broadcast();
+ }
}
void NuPlayer::Renderer::postDrainAudioQueue_l(int64_t delayUs) {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
index a521f62..3d2b033 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
@@ -18,6 +18,8 @@
#define NUPLAYER_RENDERER_H_
+#include <atomic>
+
#include <media/AudioResamplerPublic.h>
#include <media/AVSyncSettings.h>
@@ -220,6 +222,11 @@
sp<AWakeLock> mWakeLock;
+ std::atomic_flag mSyncFlag = ATOMIC_FLAG_INIT;
+ Mutex mSyncLock;
+ Condition mSyncCondition;
+ int64_t mSyncCount{0};
+
status_t getCurrentPositionOnLooper(int64_t *mediaUs);
status_t getCurrentPositionOnLooper(
int64_t *mediaUs, int64_t nowUs, bool allowPastQueuedVideo = false);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
index ee70306..b5142ed 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
@@ -154,7 +154,7 @@
}
memcpy(data,
- (const uint8_t *)mem->pointer()
+ (const uint8_t *)mem->unsecurePointer()
+ entry->mOffset,
copy);
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
index bf14ec2..83da092 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
@@ -144,6 +144,10 @@
if (mLooper == NULL) {
return;
}
+
+ // Close socket before posting message to RTSPSource message handler.
+ close(mHandler->getARTSPConnection()->getSocket());
+
sp<AMessage> msg = new AMessage(kWhatDisconnect, this);
sp<AMessage> dummy;
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/libmediaplayerservice/tests/Android.bp b/media/libmediaplayerservice/tests/Android.bp
index 8357925..e845c33 100644
--- a/media/libmediaplayerservice/tests/Android.bp
+++ b/media/libmediaplayerservice/tests/Android.bp
@@ -7,6 +7,7 @@
shared_libs: [
"liblog",
"libbinder",
+ "libbinder_ndk",
"libmedia",
"libmediaplayerservice",
"libmediadrm",
@@ -17,6 +18,10 @@
"android.hardware.drm@1.2",
],
+ static_libs: [
+ "resourcemanager_aidl_interface-ndk_platform",
+ ],
+
include_dirs: [
"frameworks/av/include",
"frameworks/av/services/mediaresourcemanager",
diff --git a/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
index 58e4bee..f114046 100644
--- a/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
+++ b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
@@ -16,25 +16,32 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "DrmSessionManager_test"
+#include <android/binder_auto_utils.h>
#include <utils/Log.h>
#include <gtest/gtest.h>
-#include <media/IResourceManagerService.h>
-#include <media/IResourceManagerClient.h>
+#include <aidl/android/media/BnResourceManagerClient.h>
+#include <aidl/android/media/BnResourceManagerService.h>
+
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/ProcessInfoInterface.h>
-#include <mediadrm/DrmHal.h>
-#include <mediadrm/DrmSessionClientInterface.h>
#include <mediadrm/DrmSessionManager.h>
#include <algorithm>
+#include <iostream>
#include <vector>
#include "ResourceManagerService.h"
namespace android {
+using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::media::BnResourceManagerClient;
+using ::aidl::android::media::BnResourceManagerService;
+using ::aidl::android::media::MediaResourceParcel;
+using ::aidl::android::media::IResourceManagerClient;
+
static Vector<uint8_t> toAndroidVector(const std::vector<uint8_t> &vec) {
Vector<uint8_t> aVec;
for (auto b : vec) {
@@ -68,21 +75,21 @@
mReclaimed(false),
mDrmSessionManager(manager) {}
- virtual ~FakeDrm() {}
-
- virtual bool reclaimResource() {
+ Status reclaimResource(bool* _aidl_return) {
mReclaimed = true;
mDrmSessionManager->removeSession(mSessionId);
- return true;
+ *_aidl_return = true;
+ return Status::ok();
}
- virtual String8 getName() {
+ Status getName(::std::string* _aidl_return) {
String8 name("FakeDrm[");
for (size_t i = 0; i < mSessionId.size(); ++i) {
name.appendFormat("%02x", mSessionId[i]);
}
name.append("]");
- return name;
+ *_aidl_return = name;
+ return Status::ok();
}
bool isReclaimed() const {
@@ -108,8 +115,7 @@
virtual void noteResetVideo() override {}
- virtual bool requestCpusetBoost(
- bool /*enable*/, const sp<IInterface> &/*client*/) override {
+ virtual bool requestCpusetBoost(bool /*enable*/) override {
return true;
}
@@ -130,14 +136,15 @@
class DrmSessionManagerTest : public ::testing::Test {
public:
DrmSessionManagerTest()
- : mService(new ResourceManagerService(new FakeProcessInfo(), new FakeSystemCallback())),
+ : mService(::ndk::SharedRefBase::make<ResourceManagerService>
+ (new FakeProcessInfo(), new FakeSystemCallback())),
mDrmSessionManager(new DrmSessionManager(mService)),
- mTestDrm1(new FakeDrm(kTestSessionId1, mDrmSessionManager)),
- mTestDrm2(new FakeDrm(kTestSessionId2, mDrmSessionManager)),
- mTestDrm3(new FakeDrm(kTestSessionId3, mDrmSessionManager)) {
- DrmSessionManager *ptr = new DrmSessionManager(mService);
- EXPECT_NE(ptr, nullptr);
- /* mDrmSessionManager = ptr; */
+ mTestDrm1(::ndk::SharedRefBase::make<FakeDrm>(
+ kTestSessionId1, mDrmSessionManager)),
+ mTestDrm2(::ndk::SharedRefBase::make<FakeDrm>(
+ kTestSessionId2, mDrmSessionManager)),
+ mTestDrm3(::ndk::SharedRefBase::make<FakeDrm>(
+ kTestSessionId3, mDrmSessionManager)) {
}
protected:
@@ -147,11 +154,11 @@
mDrmSessionManager->addSession(kTestPid2, mTestDrm3, mTestDrm3->mSessionId);
}
- sp<IResourceManagerService> mService;
+ std::shared_ptr<ResourceManagerService> mService;
sp<DrmSessionManager> mDrmSessionManager;
- sp<FakeDrm> mTestDrm1;
- sp<FakeDrm> mTestDrm2;
- sp<FakeDrm> mTestDrm3;
+ std::shared_ptr<FakeDrm> mTestDrm1;
+ std::shared_ptr<FakeDrm> mTestDrm2;
+ std::shared_ptr<FakeDrm> mTestDrm3;
};
TEST_F(DrmSessionManagerTest, addSession) {
@@ -198,7 +205,8 @@
// add a session from a higher priority process.
const std::vector<uint8_t> sid{1, 3, 5};
- sp<FakeDrm> drm = new FakeDrm(sid, mDrmSessionManager);
+ std::shared_ptr<FakeDrm> drm =
+ ::ndk::SharedRefBase::make<FakeDrm>(sid, mDrmSessionManager);
mDrmSessionManager->addSession(15, drm, drm->mSessionId);
// make sure mTestDrm2 is reclaimed next instead of mTestDrm3
diff --git a/media/libmediatranscoding/Android.bp b/media/libmediatranscoding/Android.bp
new file mode 100644
index 0000000..8f0e278
--- /dev/null
+++ b/media/libmediatranscoding/Android.bp
@@ -0,0 +1,15 @@
+// AIDL interfaces of MediaTranscoding.
+aidl_interface {
+ name: "mediatranscoding_aidl_interface",
+ local_include_dir: "aidl",
+ srcs: [
+ "aidl/android/media/IMediaTranscodingService.aidl",
+ "aidl/android/media/ITranscodingServiceClient.aidl",
+ "aidl/android/media/TranscodingErrorCode.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",
+ ],
+}
diff --git a/media/libmediatranscoding/OWNERS b/media/libmediatranscoding/OWNERS
new file mode 100644
index 0000000..02287cb
--- /dev/null
+++ b/media/libmediatranscoding/OWNERS
@@ -0,0 +1,3 @@
+akersten@google.com
+hkuang@google.com
+lnilsson@google.com
diff --git a/media/libmediatranscoding/aidl/android/media/IMediaTranscodingService.aidl b/media/libmediatranscoding/aidl/android/media/IMediaTranscodingService.aidl
new file mode 100644
index 0000000..cae8ff0
--- /dev/null
+++ b/media/libmediatranscoding/aidl/android/media/IMediaTranscodingService.aidl
@@ -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.
+ */
+
+package android.media;
+
+import android.media.TranscodingJob;
+import android.media.TranscodingRequest;
+import android.media.ITranscodingServiceClient;
+
+/**
+ * Binder interface for MediaTranscodingService.
+ *
+ * {@hide}
+ */
+interface IMediaTranscodingService {
+ /**
+ * Register the client with the MediaTranscodingService.
+ *
+ * Client must call this function to register itself with the service in order to perform
+ * transcoding. This function will return a unique positive Id assigned by the service.
+ * 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.
+ * @return a unique positive Id assigned to the client by the service, -1 means failed to
+ * register.
+ */
+ int registerClient(in ITranscodingServiceClient client);
+
+ /**
+ * Unregister the client with the MediaTranscodingService.
+ *
+ * Client will not be able to perform any more transcoding after unregister.
+ *
+ * @param clientId assigned Id of the client.
+ * @return true if succeeds, false otherwise.
+ */
+ boolean unregisterClient(in int clientId);
+
+ /**
+ * Submits a transcoding request to MediaTranscodingService.
+ *
+ * @param clientId assigned Id of the client.
+ * @param request a TranscodingRequest contains transcoding configuration.
+ * @param job(output variable) a TranscodingJob generated by the MediaTranscodingService.
+ * @return a unique positive jobId generated by the MediaTranscodingService, -1 means failure.
+ */
+ int submitRequest(in int clientId,
+ in TranscodingRequest request,
+ out TranscodingJob job);
+
+ /**
+ * Cancels a transcoding job.
+ *
+ * @param clientId assigned id of the client.
+ * @param jobId a TranscodingJob generated by the MediaTranscodingService.
+ * @return true if succeeds, false otherwise.
+ */
+ boolean cancelJob(in int clientId, in int jobId);
+
+ /**
+ * Queries the job detail associated with a jobId.
+ *
+ * @param jobId a TranscodingJob generated by the MediaTranscodingService.
+ * @param job(output variable) the TranscodingJob associated with the jobId.
+ * @return true if succeeds, false otherwise.
+ */
+ boolean getJobWithId(in int jobId, out TranscodingJob job);
+}
diff --git a/media/libmediatranscoding/aidl/android/media/ITranscodingServiceClient.aidl b/media/libmediatranscoding/aidl/android/media/ITranscodingServiceClient.aidl
new file mode 100644
index 0000000..29b6f35
--- /dev/null
+++ b/media/libmediatranscoding/aidl/android/media/ITranscodingServiceClient.aidl
@@ -0,0 +1,75 @@
+/**
+ * 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.media;
+
+import android.media.TranscodingErrorCode;
+import android.media.TranscodingJob;
+import android.media.TranscodingResult;
+
+/**
+ * ITranscodingServiceClient interface for the MediaTranscodingervice to communicate with the
+ * client.
+ *
+ * {@hide}
+ */
+//TODO(hkuang): Implement the interface.
+interface ITranscodingServiceClient {
+ /**
+ * Retrieves the name of the client.
+ */
+ @utf8InCpp String getName();
+
+ /**
+ * Called when the transcoding associated with the jobId finished.
+ *
+ * @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);
+
+ /**
+ * Called when the transcoding associated with the jobId failed.
+ *
+ * @param jobId jobId assigned by the MediaTranscodingService upon receiving request.
+ * @param errorCode error code that indicates the error.
+ */
+ oneway void onTranscodingFailed(in int jobId, in TranscodingErrorCode errorCode);
+
+ /**
+ * Called when the transcoding configuration associated with the jobId gets updated, i.e. wait
+ * number in the job queue.
+ *
+ * <p> This will only be called if client set requestUpdate to be true in the TranscodingRequest
+ * submitted to the MediaTranscodingService.
+ *
+ * @param jobId jobId assigned by the MediaTranscodingService upon receiving request.
+ * @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);
+
+ /**
+ * Called when there is an update on the progress of the TranscodingJob.
+ *
+ * <p> This will only be called if client set requestUpdate to be true in the TranscodingRequest
+ * submitted to the MediaTranscodingService.
+ *
+ * @param jobId jobId assigned by the MediaTranscodingService upon receiving request.
+ * @param progress an integer number ranging from 0 ~ 100 inclusive.
+ */
+ oneway void onProgressUpdate(in int jobId, in int progress);
+}
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingErrorCode.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingErrorCode.aidl
new file mode 100644
index 0000000..7f47fdc
--- /dev/null
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingErrorCode.aidl
@@ -0,0 +1,33 @@
+/**
+ * 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.media;
+
+/**
+ * Type enums of video transcoding errors.
+ *
+ * {@hide}
+ */
+@Backing(type = "int")
+enum TranscodingErrorCode {
+ kUnknown = 0,
+ kUnsupported = 1,
+ kDecoderError = 2,
+ kEncoderError = 3,
+ kExtractorError = 4,
+ kMuxerError = 5,
+ kInvalidBitstream = 6
+}
\ No newline at end of file
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingJob.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingJob.aidl
new file mode 100644
index 0000000..42ad8ad
--- /dev/null
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingJob.aidl
@@ -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.
+ */
+
+package android.media;
+
+import android.media.TranscodingRequest;
+
+/**
+ * TranscodingJob is generated by the MediaTranscodingService upon receiving a TranscodingRequest.
+ * It contains all the necessary configuration generated by the MediaTranscodingService for the
+ * TranscodingRequest.
+ *
+ * {@hide}
+ */
+//TODO(hkuang): Implement the parcelable.
+parcelable TranscodingJob {
+ /**
+ * A unique positive Id generated by the MediaTranscodingService.
+ */
+ int jobId;
+
+ /**
+ * The request associated with the TranscodingJob.
+ */
+ TranscodingRequest request;
+
+ /**
+ * Current number of jobs ahead of this job. The service schedules the job based on the priority
+ * passed from the client. Client could specify whether to receive updates when the
+ * awaitNumberOfJobs changes through setting requestProgressUpdate in the TranscodingRequest.
+ */
+ int awaitNumberOfJobs;
+}
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingRequest.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingRequest.aidl
new file mode 100644
index 0000000..6b51ee3
--- /dev/null
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingRequest.aidl
@@ -0,0 +1,58 @@
+/**
+ * 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.media;
+
+import android.media.TranscodingType;
+
+/**
+ * TranscodingRequest contains the desired configuration for the transcoding.
+ *
+ * {@hide}
+ */
+//TODO(hkuang): Implement the parcelable.
+parcelable TranscodingRequest {
+ /**
+ * Name of file to be transcoded.
+ */
+ @utf8InCpp String fileName;
+
+ /**
+ * Type of the transcoding.
+ */
+ TranscodingType transcodingType;
+
+ /**
+ * Input source file descriptor.
+ */
+ ParcelFileDescriptor inFd;
+
+ /**
+ * Output transcoded file descriptor.
+ */
+ ParcelFileDescriptor outFd;
+
+ /**
+ * Priority of this transcoding. Service will schedule the transcoding based on the priority.
+ */
+ // TODO(hkuang): Define the priority level.
+ int priority;
+
+ /**
+ * Whether to receive update on progress and change of awaitNumJobs.
+ */
+ boolean requestUpdate;
+}
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingResult.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingResult.aidl
new file mode 100644
index 0000000..a41e025
--- /dev/null
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingResult.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.media;
+
+import android.media.TranscodingJob;
+
+/**
+ * Result of the transcoding.
+ *
+ * {@hide}
+ */
+//TODO(hkuang): Implement the parcelable.
+parcelable TranscodingResult {
+ /**
+ * The jobId associated with the TranscodingResult.
+ */
+ int jobId;
+
+ /**
+ * Actual bitrate of the transcoded video in bits per second. This will only present for video
+ * transcoding. -1 means not available.
+ */
+ int actualBitrateBps;
+
+ // TODO(hkuang): Add more fields.
+}
\ No newline at end of file
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingType.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingType.aidl
new file mode 100644
index 0000000..9184c87
--- /dev/null
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingType.aidl
@@ -0,0 +1,29 @@
+/**
+ * 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.media;
+
+/**
+ * Type of transcoding.
+ *
+ * {@hide}
+ */
+@Backing(type = "int")
+enum TranscodingType {
+ kUnknown = 0,
+ kVideoTranscoding = 1,
+ kImageTranscoding = 2,
+}
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingVideoCodecType.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingVideoCodecType.aidl
new file mode 100644
index 0000000..5dab4f2
--- /dev/null
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingVideoCodecType.aidl
@@ -0,0 +1,29 @@
+/**
+ * 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.media;
+
+/**
+ * Type enums of video codec type.
+ *
+ * {@hide}
+ */
+@Backing(type = "int")
+enum TranscodingVideoCodecType {
+ kUnspecified = 0,
+ kAvc = 1,
+ kHevc = 2,
+}
\ No newline at end of file
diff --git a/media/libnblog/Reader.cpp b/media/libnblog/Reader.cpp
index f556e37..67d028d 100644
--- a/media/libnblog/Reader.cpp
+++ b/media/libnblog/Reader.cpp
@@ -45,7 +45,12 @@
}
Reader::Reader(const sp<IMemory>& iMemory, size_t size, const std::string &name)
- : Reader(iMemory != 0 ? (Shared *) iMemory->pointer() : NULL, size, name)
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ : Reader(iMemory != 0 ? (Shared *) iMemory->unsecurePointer() : NULL, size,
+ name)
{
mIMemory = iMemory;
}
@@ -156,7 +161,8 @@
bool Reader::isIMemory(const sp<IMemory>& iMemory) const
{
- return iMemory != 0 && mIMemory != 0 && iMemory->pointer() == mIMemory->pointer();
+ return iMemory != 0 && mIMemory != 0 &&
+ iMemory->unsecurePointer() == mIMemory->unsecurePointer();
}
// We make a set of the invalid types rather than the valid types when aligning
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/libnblog/Writer.cpp b/media/libnblog/Writer.cpp
index da3bd52..86d3b98 100644
--- a/media/libnblog/Writer.cpp
+++ b/media/libnblog/Writer.cpp
@@ -56,7 +56,11 @@
}
Writer::Writer(const sp<IMemory>& iMemory, size_t size)
- : Writer(iMemory != 0 ? (Shared *) iMemory->pointer() : NULL, size)
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ : Writer(iMemory != 0 ? (Shared *) iMemory->unsecurePointer() : NULL, size)
{
mIMemory = iMemory;
}
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 eacaea8..ef9d253 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -24,6 +24,8 @@
#include <inttypes.h>
#include <utils/Trace.h>
+#include <android/hardware/media/omx/1.0/IGraphicBufferSource.h>
+
#include <gui/Surface.h>
#include <media/stagefright/ACodec.h>
@@ -45,6 +47,7 @@
#include <media/hardware/HardwareAPI.h>
#include <media/MediaBufferHolder.h>
#include <media/OMXBuffer.h>
+#include <media/omx/1.0/Conversion.h>
#include <media/omx/1.0/WOmxNode.h>
#include <hidlmemory/mapping.h>
@@ -63,7 +66,9 @@
namespace android {
-using binder::Status;
+typedef hardware::media::omx::V1_0::IGraphicBufferSource HGraphicBufferSource;
+
+using hardware::media::omx::V1_0::Status;
enum {
kMaxIndicesToCheck = 32, // used when enumerating supported formats and profiles
@@ -98,16 +103,11 @@
}
}
-static inline status_t statusFromBinderStatus(const Status &status) {
+static inline status_t statusFromBinderStatus(hardware::Return<Status> &&status) {
if (status.isOk()) {
- return OK;
- }
- status_t err;
- if ((err = status.serviceSpecificErrorCode()) != OK) {
- return err;
- }
- if ((err = status.transactionError()) != OK) {
- return err;
+ return static_cast<status_t>(status.withDefault(Status::UNKNOWN_ERROR));
+ } else if (status.isDeadObject()) {
+ return DEAD_OBJECT;
}
// Other exception
return UNKNOWN_ERROR;
@@ -1780,6 +1780,14 @@
}
}
+ int32_t lowLatency = 0;
+ if (msg->findInt32("low-latency", &lowLatency)) {
+ err = setLowLatency(lowLatency);
+ if (err != OK) {
+ return err;
+ }
+ }
+
int32_t prependSPSPPS = 0;
if (encoder && mIsVideo
&& msg->findInt32("prepend-sps-pps-to-idr-frames", &prependSPSPPS)
@@ -2348,6 +2356,24 @@
return err;
}
+status_t ACodec::setLowLatency(int32_t lowLatency) {
+ if (mIsEncoder) {
+ ALOGE("encoder does not support low-latency");
+ return BAD_VALUE;
+ }
+
+ OMX_CONFIG_BOOLEANTYPE config;
+ InitOMXParams(&config);
+ config.bEnabled = (OMX_BOOL)(lowLatency != 0);
+ status_t err = mOMXNode->setConfig(
+ (OMX_INDEXTYPE)OMX_IndexConfigLowLatency,
+ &config, sizeof(config));
+ if (err != OK) {
+ ALOGE("decoder can not set low-latency to %d (err %d)", lowLatency, err);
+ }
+ return err;
+}
+
status_t ACodec::setLatency(uint32_t latency) {
OMX_PARAM_U32TYPE config;
InitOMXParams(&config);
@@ -2410,7 +2436,7 @@
}
rate = (OMX_U32)(rateFloat * 65536.0f + 0.5f);
} else {
- if (rateFloat > static_cast<float>(UINT_MAX)) {
+ if (rateFloat > (float)UINT_MAX) {
return BAD_VALUE;
}
rate = (OMX_U32)(rateFloat);
@@ -6881,8 +6907,11 @@
return err;
}
+ using hardware::media::omx::V1_0::utils::TWOmxNode;
err = statusFromBinderStatus(
- mCodec->mGraphicBufferSource->configure(mCodec->mOMXNode, dataSpace));
+ mCodec->mGraphicBufferSource->configure(
+ new TWOmxNode(mCodec->mOMXNode),
+ static_cast<hardware::graphics::common::V1_0::Dataspace>(dataSpace)));
if (err != OK) {
ALOGE("[%s] Unable to configure for node (err %d)",
mCodec->mComponentName.c_str(), err);
@@ -6968,8 +6997,9 @@
}
err = statusFromBinderStatus(
- mCodec->mGraphicBufferSource->setColorAspects(ColorUtils::packToU32(
- *(ColorAspects *)colorAspectsBuffer->base())));
+ mCodec->mGraphicBufferSource->setColorAspects(
+ hardware::media::omx::V1_0::utils::toHardwareColorAspects(
+ *(ColorAspects *)colorAspectsBuffer->base())));
if (err != OK) {
ALOGE("[%s] Unable to configure color aspects (err %d)",
@@ -6985,8 +7015,10 @@
ALOGV("onCreateInputSurface");
sp<IGraphicBufferProducer> bufferProducer;
+ sp<HGraphicBufferSource> bufferSource;
status_t err = mCodec->mOMX->createInputSurface(
- &bufferProducer, &mCodec->mGraphicBufferSource);
+ &bufferProducer, &bufferSource);
+ mCodec->mGraphicBufferSource = bufferSource;
if (err == OK) {
err = setupInputSurface();
@@ -7019,8 +7051,12 @@
}
sp<PersistentSurface> surface = static_cast<PersistentSurface *>(obj.get());
- mCodec->mGraphicBufferSource = surface->getBufferSource();
- status_t err = setupInputSurface();
+ sp<HGraphicBufferSource> hgbs = HGraphicBufferSource::castFrom(surface->getHidlTarget());
+ status_t err = BAD_VALUE;
+ if (hgbs) {
+ mCodec->mGraphicBufferSource = hgbs;
+ err = setupInputSurface();
+ }
if (err == OK) {
mCodec->mCallback->onInputSurfaceAccepted(
@@ -7539,8 +7575,14 @@
}
int64_t stopTimeOffsetUs;
- err = statusFromBinderStatus(
- mGraphicBufferSource->getStopTimeOffsetUs(&stopTimeOffsetUs));
+ hardware::Return<void> trans = mGraphicBufferSource->getStopTimeOffsetUs(
+ [&err, &stopTimeOffsetUs](auto status, auto result) {
+ err = static_cast<status_t>(status);
+ stopTimeOffsetUs = result;
+ });
+ if (!trans.isOk()) {
+ err = trans.isDeadObject() ? DEAD_OBJECT : UNKNOWN_ERROR;
+ }
if (err != OK) {
ALOGE("Failed to get stop time offset (err %d)", err);
@@ -7583,6 +7625,14 @@
}
}
+ int32_t lowLatency = 0;
+ if (params->findInt32("low-latency", &lowLatency)) {
+ status_t err = setLowLatency(lowLatency);
+ if (err != OK) {
+ return err;
+ }
+ }
+
int32_t latency = 0;
if (params->findInt32("latency", &latency) && latency > 0) {
status_t err = setLatency(latency);
diff --git a/media/libstagefright/ACodecBufferChannel.cpp b/media/libstagefright/ACodecBufferChannel.cpp
index 266a240..bf8f09c 100644
--- a/media/libstagefright/ACodecBufferChannel.cpp
+++ b/media/libstagefright/ACodecBufferChannel.cpp
@@ -21,6 +21,7 @@
#include <numeric>
#include <android/hardware/cas/native/1.0/IDescrambler.h>
+#include <android/hardware/drm/1.0/types.h>
#include <binder/MemoryDealer.h>
#include <hidlmemory/FrameworkUtils.h>
#include <media/openmax/OMX_Core.h>
@@ -41,6 +42,7 @@
using hardware::hidl_vec;
using namespace hardware::cas::V1_0;
using namespace hardware::cas::native::V1_0;
+using DrmBufferType = hardware::drm::V1_0::BufferType;
using BufferInfo = ACodecBufferChannel::BufferInfo;
using BufferInfoIterator = std::vector<const BufferInfo>::const_iterator;
@@ -131,18 +133,18 @@
ssize_t result = -1;
ssize_t codecDataOffset = 0;
if (mCrypto != NULL) {
- ICrypto::DestinationBuffer destination;
+ hardware::drm::V1_0::DestinationBuffer destination;
if (secure) {
- destination.mType = ICrypto::kDestinationTypeNativeHandle;
- destination.mHandle = secureHandle;
+ destination.type = DrmBufferType::NATIVE_HANDLE;
+ destination.secureMemory = hidl_handle(secureHandle);
} else {
- destination.mType = ICrypto::kDestinationTypeSharedMemory;
- destination.mSharedMemory = mDecryptDestination;
+ destination.type = DrmBufferType::SHARED_MEMORY;
+ IMemoryToSharedBuffer(
+ mDecryptDestination, mHeapSeqNum, &destination.nonsecureMemory);
}
- ICrypto::SourceBuffer source;
- source.mSharedMemory = it->mSharedEncryptedBuffer;
- source.mHeapSeqNum = mHeapSeqNum;
+ hardware::drm::V1_0::SharedBuffer source;
+ IMemoryToSharedBuffer(it->mSharedEncryptedBuffer, mHeapSeqNum, &source);
result = mCrypto->decrypt(key, iv, mode, pattern,
source, it->mClientBuffer->offset(),
@@ -152,8 +154,8 @@
return result;
}
- if (destination.mType == ICrypto::kDestinationTypeSharedMemory) {
- memcpy(it->mCodecBuffer->base(), destination.mSharedMemory->pointer(), 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
@@ -219,7 +221,8 @@
if (dstBuffer.type == BufferType::SHARED_MEMORY) {
memcpy(it->mCodecBuffer->base(),
- (uint8_t*)it->mSharedEncryptedBuffer->pointer(), result);
+ (uint8_t*)it->mSharedEncryptedBuffer->unsecurePointer(),
+ result);
}
}
@@ -313,7 +316,8 @@
}
dealer = new MemoryDealer(heapSize, "ACodecBufferChannel");
if (mCrypto != nullptr) {
- int32_t seqNum = mCrypto->setHeap(dealer->getMemoryHeap());
+ sp<HidlMemory> hHeap = fromHeap(dealer->getMemoryHeap());
+ int32_t seqNum = mCrypto->setHeap(hHeap);
if (seqNum >= 0) {
mHeapSeqNum = seqNum;
ALOGV("setHeap returned mHeapSeqNum=%d", mHeapSeqNum);
@@ -429,4 +433,12 @@
it->mClientBuffer);
}
+void ACodecBufferChannel::setCrypto(const sp<ICrypto> &crypto) {
+ mCrypto = crypto;
+}
+
+void ACodecBufferChannel::setDescrambler(const sp<IDescrambler> &descrambler) {
+ mDescrambler = descrambler;
+}
+
} // namespace android
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 18dacb8..1f5ec55 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -53,6 +53,7 @@
"CodecBase.cpp",
"FrameRenderTracker.cpp",
"MediaCodecListWriter.cpp",
+ "SkipCutBuffer.cpp",
],
cflags: [
@@ -62,16 +63,20 @@
header_libs: [
"libmediadrm_headers",
+ "media_ndk_headers",
],
shared_libs: [
"libgui",
+ "libhidlallocatorutils",
"liblog",
"libmedia_codeclist",
+ "libmedia_omx",
"libstagefright_foundation",
"libui",
"libutils",
"android.hardware.cas.native@1.0",
+ "android.hardware.drm@1.0",
],
sanitize: {
@@ -122,6 +127,53 @@
},
}
+cc_library_shared {
+ name: "libstagefright_framecapture_utils",
+
+ srcs: [
+ "FrameCaptureLayer.cpp",
+ "FrameCaptureProcessor.cpp",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "libEGL",
+ "libGLESv1_CM",
+ "libGLESv2",
+ "libgui",
+ "liblog",
+ "libprocessgroup",
+ "libstagefright_foundation",
+ "libsync",
+ "libui",
+ "libutils",
+ ],
+
+ static_libs: [
+ "librenderengine",
+ ],
+
+ export_include_dirs: [
+ "include",
+ ],
+
+ cflags: [
+ "-Wno-multichar",
+ "-Werror",
+ "-Wno-error=deprecated-declarations",
+ "-Wall",
+ ],
+
+ sanitize: {
+ // TODO: re-enabled cfi for this lib after b/139945549 fixed
+ cfi: false,
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ },
+}
cc_library {
name: "libstagefright",
@@ -163,9 +215,7 @@
"RemoteMediaExtractor.cpp",
"RemoteMediaSource.cpp",
"SimpleDecodingSource.cpp",
- "SkipCutBuffer.cpp",
"StagefrightMediaScanner.cpp",
- "StagefrightPluginLoader.cpp",
"SurfaceUtils.cpp",
"ThrottledSource.cpp",
"Utils.cpp",
@@ -174,9 +224,11 @@
],
shared_libs: [
+ "libstagefright_framecapture_utils",
"libaudioutils",
"libbase",
"libbinder",
+ "libbinder_ndk",
"libcamera_client",
"libcutils",
"libdatasource",
@@ -190,10 +242,10 @@
"libmedia_omx_client",
"libaudioclient",
"libmediametrics",
- "libmediautils",
"libui",
"libutils",
"libmedia_helper",
+ "libsfplugin_ccodec",
"libstagefright_codecbase",
"libstagefright_foundation",
"libstagefright_omx_utils",
@@ -203,6 +255,7 @@
"libhidlmemory",
"android.hidl.allocator@1.0",
"android.hardware.cas.native@1.0",
+ "android.hardware.drm@1.0",
"android.hardware.media.omx@1.0",
],
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index d78d729..00c5b40 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -50,7 +50,7 @@
}
AudioSource::AudioSource(
- audio_source_t inputSource, const String16 &opPackageName,
+ const audio_attributes_t *attr, const String16 &opPackageName,
uint32_t sampleRate, uint32_t channelCount, uint32_t outSampleRate,
uid_t uid, pid_t pid, audio_port_handle_t selectedDeviceId,
audio_microphone_direction_t selectedMicDirection,
@@ -92,7 +92,7 @@
}
mRecord = new AudioRecord(
- inputSource, sampleRate, AUDIO_FORMAT_PCM_16_BIT,
+ AUDIO_SOURCE_DEFAULT, sampleRate, AUDIO_FORMAT_PCM_16_BIT,
audio_channel_in_mask_from_count(channelCount),
opPackageName,
(size_t) (bufCount * frameCount),
@@ -104,7 +104,7 @@
AUDIO_INPUT_FLAG_NONE,
uid,
pid,
- NULL /*pAttributes*/,
+ attr,
selectedDeviceId,
selectedMicDirection,
selectedMicFieldDimension);
diff --git a/media/libstagefright/BufferImpl.cpp b/media/libstagefright/BufferImpl.cpp
index f73b625..b097324 100644
--- a/media/libstagefright/BufferImpl.cpp
+++ b/media/libstagefright/BufferImpl.cpp
@@ -32,7 +32,11 @@
// SharedMemoryBuffer
SharedMemoryBuffer::SharedMemoryBuffer(const sp<AMessage> &format, const sp<IMemory> &mem)
- : MediaCodecBuffer(format, new ABuffer(mem->pointer(), mem->size())),
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ : MediaCodecBuffer(format, new ABuffer(mem->unsecurePointer(), mem->size())),
mMemory(mem) {
}
diff --git a/media/libstagefright/CallbackDataSource.cpp b/media/libstagefright/CallbackDataSource.cpp
index 265f21b..eb3cb45 100644
--- a/media/libstagefright/CallbackDataSource.cpp
+++ b/media/libstagefright/CallbackDataSource.cpp
@@ -20,9 +20,9 @@
#include "include/CallbackDataSource.h"
+#include <android/IDataSource.h>
#include <binder/IMemory.h>
#include <binder/IPCThreadState.h>
-#include <media/IDataSource.h>
#include <media/stagefright/foundation/ADebug.h>
#include <algorithm>
@@ -81,7 +81,8 @@
return ERROR_OUT_OF_RANGE;
}
CHECK(numRead >= 0 && (size_t)numRead <= bufferSize);
- memcpy(((uint8_t*)data) + totalNumRead, mMemory->pointer(), numRead);
+ memcpy(((uint8_t*)data) + totalNumRead, mMemory->unsecurePointer(),
+ numRead);
numLeft -= numRead;
totalNumRead += numRead;
}
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 41f5db0..9b3f420 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -89,7 +89,7 @@
void CameraSourceListener::postData(int32_t msgType, const sp<IMemory> &dataPtr,
camera_frame_metadata_t * /* metadata */) {
ALOGV("postData(%d, ptr:%p, size:%zu)",
- msgType, dataPtr->pointer(), dataPtr->size());
+ msgType, dataPtr->unsecurePointer(), dataPtr->size());
sp<CameraSource> source = mSource.promote();
if (source.get() != NULL) {
@@ -966,8 +966,12 @@
// Check if frame contains a VideoNativeHandleMetadata.
if (frame->size() == sizeof(VideoNativeHandleMetadata)) {
- VideoNativeHandleMetadata *metadata =
- (VideoNativeHandleMetadata*)(frame->pointer());
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ VideoNativeHandleMetadata *metadata =
+ (VideoNativeHandleMetadata*)(frame->unsecurePointer());
if (metadata->eType == kMetadataBufferTypeNativeHandleSource) {
handle = metadata->pHandle;
}
@@ -1047,7 +1051,7 @@
Mutex::Autolock autoLock(mLock);
for (List<sp<IMemory> >::iterator it = mFramesBeingEncoded.begin();
it != mFramesBeingEncoded.end(); ++it) {
- if ((*it)->pointer() == buffer->data()) {
+ if ((*it)->unsecurePointer() == buffer->data()) {
releaseOneRecordingFrame((*it));
mFramesBeingEncoded.erase(it);
++mNumFramesEncoded;
@@ -1102,7 +1106,11 @@
frameTime = *mFrameTimes.begin();
mFrameTimes.erase(mFrameTimes.begin());
mFramesBeingEncoded.push_back(frame);
- *buffer = new MediaBuffer(frame->pointer(), frame->size());
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ *buffer = new MediaBuffer(frame->unsecurePointer(), frame->size());
(*buffer)->setObserver(this);
(*buffer)->add_ref();
(*buffer)->meta_data().setInt64(kKeyTime, frameTime);
@@ -1248,7 +1256,7 @@
mMemoryBases.erase(mMemoryBases.begin());
// Wrap native handle in sp<IMemory> so it can be pushed to mFramesReceived.
- VideoNativeHandleMetadata *metadata = (VideoNativeHandleMetadata*)(data->pointer());
+ VideoNativeHandleMetadata *metadata = (VideoNativeHandleMetadata*)(data->unsecurePointer());
metadata->eType = kMetadataBufferTypeNativeHandleSource;
metadata->pHandle = handle;
@@ -1296,7 +1304,11 @@
mMemoryBases.erase(mMemoryBases.begin());
// Wrap native handle in sp<IMemory> so it can be pushed to mFramesReceived.
- VideoNativeHandleMetadata *metadata = (VideoNativeHandleMetadata*)(data->pointer());
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ VideoNativeHandleMetadata *metadata = (VideoNativeHandleMetadata*)(data->unsecurePointer());
metadata->eType = kMetadataBufferTypeNativeHandleSource;
metadata->pHandle = handle;
diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp
index 2a819ad..e0a6eb3 100644
--- a/media/libstagefright/CameraSourceTimeLapse.cpp
+++ b/media/libstagefright/CameraSourceTimeLapse.cpp
@@ -245,11 +245,11 @@
ALOGV("createIMemoryCopy");
size_t source_size = source_data->size();
- void* source_pointer = source_data->pointer();
+ void* source_pointer = source_data->unsecurePointer();
sp<MemoryHeapBase> newMemoryHeap = new MemoryHeapBase(source_size);
sp<MemoryBase> newMemory = new MemoryBase(newMemoryHeap, 0, source_size);
- memcpy(newMemory->pointer(), source_pointer, source_size);
+ memcpy(newMemory->unsecurePointer(), source_pointer, source_size);
return newMemory;
}
diff --git a/media/libstagefright/CodecBase.cpp b/media/libstagefright/CodecBase.cpp
index 97f38f8..5b724aa 100644
--- a/media/libstagefright/CodecBase.cpp
+++ b/media/libstagefright/CodecBase.cpp
@@ -18,18 +18,26 @@
#define LOG_TAG "CodecBase"
#include <android/hardware/cas/native/1.0/IDescrambler.h>
+#include <android/hardware/drm/1.0/types.h>
+#include <hidlmemory/FrameworkUtils.h>
#include <mediadrm/ICrypto.h>
#include <media/stagefright/CodecBase.h>
#include <utils/Log.h>
namespace android {
-void BufferChannelBase::setCrypto(const sp<ICrypto> &crypto) {
- mCrypto = crypto;
-}
+void BufferChannelBase::IMemoryToSharedBuffer(
+ const sp<IMemory> &memory,
+ int32_t heapSeqNum,
+ hardware::drm::V1_0::SharedBuffer *buf) {
+ ssize_t offset;
+ size_t size;
-void BufferChannelBase::setDescrambler(const sp<IDescrambler> &descrambler) {
- mDescrambler = descrambler;
+ sp<hardware::HidlMemory> hidlMemory;
+ hidlMemory = hardware::fromHeap(memory->getMemory(&offset, &size));
+ buf->bufferId = static_cast<uint32_t>(heapSeqNum);
+ buf->offset = offset >= 0 ? offset : 0;
+ buf->size = size;
}
} // namespace android
diff --git a/media/libstagefright/FrameCaptureLayer.cpp b/media/libstagefright/FrameCaptureLayer.cpp
new file mode 100644
index 0000000..815057d
--- /dev/null
+++ b/media/libstagefright/FrameCaptureLayer.cpp
@@ -0,0 +1,257 @@
+/*
+ * 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 "FrameCaptureLayer"
+
+#include <include/FrameCaptureLayer.h>
+#include <media/stagefright/FrameCaptureProcessor.h>
+#include <gui/BufferQueue.h>
+#include <gui/GLConsumer.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <gui/Surface.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaErrors.h>
+#include <renderengine/RenderEngine.h>
+#include <utils/Log.h>
+
+namespace android {
+
+static const int64_t kAcquireBufferTimeoutNs = 100000000LL;
+static constexpr float kDefaultMaxMasteringLuminance = 1000.0;
+static constexpr float kDefaultMaxContentLuminance = 1000.0;
+
+ui::Dataspace translateDataspace(ui::Dataspace dataspace) {
+ ui::Dataspace updatedDataspace = dataspace;
+ // translate legacy dataspaces to modern dataspaces
+ switch (dataspace) {
+ case ui::Dataspace::SRGB:
+ updatedDataspace = ui::Dataspace::V0_SRGB;
+ break;
+ case ui::Dataspace::SRGB_LINEAR:
+ updatedDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+ break;
+ case ui::Dataspace::JFIF:
+ updatedDataspace = ui::Dataspace::V0_JFIF;
+ break;
+ case ui::Dataspace::BT601_625:
+ updatedDataspace = ui::Dataspace::V0_BT601_625;
+ break;
+ case ui::Dataspace::BT601_525:
+ updatedDataspace = ui::Dataspace::V0_BT601_525;
+ break;
+ case ui::Dataspace::BT709:
+ updatedDataspace = ui::Dataspace::V0_BT709;
+ break;
+ default:
+ break;
+ }
+
+ return updatedDataspace;
+}
+
+bool isHdrY410(const BufferItem &bi) {
+ ui::Dataspace dataspace = translateDataspace(static_cast<ui::Dataspace>(bi.mDataSpace));
+ // pixel format is HDR Y410 masquerading as RGBA_1010102
+ return (dataspace == ui::Dataspace::BT2020_ITU_PQ &&
+ bi.mGraphicBuffer->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102);
+}
+
+struct FrameCaptureLayer::BufferLayer : public FrameCaptureProcessor::Layer {
+ BufferLayer(const BufferItem &bi) : mBufferItem(bi) {}
+ void getLayerSettings(
+ const Rect &sourceCrop, uint32_t textureName,
+ renderengine::LayerSettings *layerSettings) override;
+ BufferItem mBufferItem;
+};
+
+void FrameCaptureLayer::BufferLayer::getLayerSettings(
+ const Rect &sourceCrop, uint32_t textureName,
+ renderengine::LayerSettings *layerSettings) {
+ layerSettings->geometry.boundaries = sourceCrop.toFloatRect();
+ layerSettings->alpha = 1.0f;
+
+ layerSettings->sourceDataspace = translateDataspace(
+ static_cast<ui::Dataspace>(mBufferItem.mDataSpace));
+
+ // from BufferLayer
+ layerSettings->source.buffer.buffer = mBufferItem.mGraphicBuffer;
+ layerSettings->source.buffer.isOpaque = true;
+ layerSettings->source.buffer.fence = mBufferItem.mFence;
+ layerSettings->source.buffer.textureName = textureName;
+ layerSettings->source.buffer.usePremultipliedAlpha = false;
+ layerSettings->source.buffer.isY410BT2020 = isHdrY410(mBufferItem);
+ bool hasSmpte2086 = mBufferItem.mHdrMetadata.validTypes & HdrMetadata::SMPTE2086;
+ bool hasCta861_3 = mBufferItem.mHdrMetadata.validTypes & HdrMetadata::CTA861_3;
+ layerSettings->source.buffer.maxMasteringLuminance = hasSmpte2086
+ ? mBufferItem.mHdrMetadata.smpte2086.maxLuminance
+ : kDefaultMaxMasteringLuminance;
+ layerSettings->source.buffer.maxContentLuminance = hasCta861_3
+ ? mBufferItem.mHdrMetadata.cta8613.maxContentLightLevel
+ : kDefaultMaxContentLuminance;
+
+ // Set filtering to false since the capture itself doesn't involve
+ // any scaling, metadata retriever JNI is scaling the bitmap if
+ // display size is different from decoded size. If that scaling
+ // needs to be handled by server side, consider enable this based
+ // display size vs decoded size.
+ const bool useFiltering = false;
+ layerSettings->source.buffer.useTextureFiltering = useFiltering;
+
+ float textureMatrix[16];
+ GLConsumer::computeTransformMatrix(
+ textureMatrix, mBufferItem.mGraphicBuffer,
+ mBufferItem.mCrop, mBufferItem.mTransform, useFiltering);
+
+ // Flip y-coordinates because GLConsumer expects OpenGL convention.
+ mat4 tr = mat4::translate(vec4(.5, .5, 0, 1)) * mat4::scale(vec4(1, -1, 1, 1)) *
+ mat4::translate(vec4(-.5, -.5, 0, 1));
+
+ layerSettings->source.buffer.textureTransform =
+ mat4(static_cast<const float*>(textureMatrix)) * tr;
+}
+
+status_t FrameCaptureLayer::init() {
+ if (FrameCaptureProcessor::getInstance() == nullptr) {
+ ALOGE("failed to get capture processor");
+ return ERROR_UNSUPPORTED;
+ }
+
+ // Mimic surfaceflinger's BufferQueueLayer::onFirstRef() to create a
+ // BufferQueue for encoder output
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+
+ BufferQueue::createBufferQueue(&producer, &consumer);
+ // We don't need HW_COMPOSER usage since we're not using hwc to compose.
+ // The buffer is only used as a GL texture.
+ consumer->setConsumerUsageBits(GraphicBuffer::USAGE_HW_TEXTURE);
+ consumer->setConsumerName(String8("FrameDecoder"));
+
+ status_t err = consumer->consumerConnect(
+ new BufferQueue::ProxyConsumerListener(this), false);
+ if (NO_ERROR != err) {
+ ALOGE("Error connecting to BufferQueue: %s (%d)", strerror(-err), err);
+ return err;
+ }
+
+ mConsumer = consumer;
+ mSurface = new Surface(producer);
+
+ return OK;
+}
+
+status_t FrameCaptureLayer::capture(const ui::PixelFormat reqPixelFormat,
+ const Rect &sourceCrop, sp<GraphicBuffer> *outBuffer) {
+ ALOGV("capture: reqPixelFormat %d, crop {%d, %d, %d, %d}", reqPixelFormat,
+ sourceCrop.left, sourceCrop.top, sourceCrop.right, sourceCrop.bottom);
+
+ BufferItem bi;
+ status_t err = acquireBuffer(&bi);
+ if (err != OK) {
+ return err;
+ }
+
+ // create out buffer
+ const uint32_t usage =
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
+ GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
+ sp<GraphicBuffer> buffer = new GraphicBuffer(
+ sourceCrop.getWidth(), sourceCrop.getHeight(),
+ static_cast<android_pixel_format>(reqPixelFormat),
+ 1, usage, std::string("thumbnail"));
+
+ err = FrameCaptureProcessor::getInstance()->capture(
+ new BufferLayer(bi), sourceCrop, buffer);
+ if (err == OK) {
+ *outBuffer = buffer;
+ }
+
+ (void)releaseBuffer(bi);
+ return err;
+}
+
+FrameCaptureLayer::FrameCaptureLayer() : mFrameAvailable(false) {}
+
+void FrameCaptureLayer::onFrameAvailable(const BufferItem& /*item*/) {
+ ALOGV("onFrameAvailable");
+ Mutex::Autolock _lock(mLock);
+
+ mFrameAvailable = true;
+ mCondition.signal();
+}
+
+void FrameCaptureLayer::onBuffersReleased() {
+ ALOGV("onBuffersReleased");
+ Mutex::Autolock _lock(mLock);
+
+ uint64_t mask = 0;
+ mConsumer->getReleasedBuffers(&mask);
+ for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+ if (mask & (1ULL << i)) {
+ mSlotToBufferMap[i] = nullptr;
+ }
+ }
+}
+
+void FrameCaptureLayer::onSidebandStreamChanged() {
+ ALOGV("onSidebandStreamChanged");
+}
+
+status_t FrameCaptureLayer::acquireBuffer(BufferItem *bi) {
+ ALOGV("acquireBuffer");
+ Mutex::Autolock _lock(mLock);
+
+ if (!mFrameAvailable) {
+ // The output buffer is already released to the codec at this point.
+ // Use a small timeout of 100ms in case the buffer hasn't arrived
+ // at the consumer end of the output surface yet.
+ if (mCondition.waitRelative(mLock, kAcquireBufferTimeoutNs) != OK) {
+ ALOGE("wait for buffer timed out");
+ return TIMED_OUT;
+ }
+ }
+ mFrameAvailable = false;
+
+ status_t err = mConsumer->acquireBuffer(bi, 0);
+ if (err != OK) {
+ ALOGE("failed to acquire buffer!");
+ return err;
+ }
+
+ if (bi->mGraphicBuffer != nullptr) {
+ mSlotToBufferMap[bi->mSlot] = bi->mGraphicBuffer;
+ } else {
+ bi->mGraphicBuffer = mSlotToBufferMap[bi->mSlot];
+ }
+
+ if (bi->mGraphicBuffer == nullptr) {
+ ALOGE("acquired null buffer!");
+ return BAD_VALUE;
+ }
+ return OK;
+}
+
+status_t FrameCaptureLayer::releaseBuffer(const BufferItem &bi) {
+ ALOGV("releaseBuffer");
+ Mutex::Autolock _lock(mLock);
+
+ return mConsumer->releaseBuffer(bi.mSlot, bi.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, bi.mFence);
+}
+
+} // namespace android
diff --git a/media/libstagefright/FrameCaptureProcessor.cpp b/media/libstagefright/FrameCaptureProcessor.cpp
new file mode 100644
index 0000000..c517e33
--- /dev/null
+++ b/media/libstagefright/FrameCaptureProcessor.cpp
@@ -0,0 +1,213 @@
+/*
+ * 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 "FrameCaptureProcessor"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/FrameCaptureProcessor.h>
+#include <media/stagefright/MediaErrors.h>
+#include <renderengine/RenderEngine.h>
+#include <ui/Fence.h>
+#include <ui/PixelFormat.h>
+#include <utils/Log.h>
+
+namespace android {
+
+//static
+Mutex FrameCaptureProcessor::sLock;
+//static
+sp<FrameCaptureProcessor> FrameCaptureProcessor::sInstance;
+
+//static
+sp<FrameCaptureProcessor> FrameCaptureProcessor::getInstance() {
+ Mutex::Autolock _l(sLock);
+ if (sInstance == nullptr) {
+ sInstance = new FrameCaptureProcessor();
+ sInstance->createRenderEngine();
+ }
+ // init only once, if failed nullptr will be returned afterwards.
+ return (sInstance->initCheck() == OK) ? sInstance : nullptr;
+}
+
+//static
+status_t FrameCaptureProcessor::PostAndAwaitResponse(
+ const sp<AMessage> &msg, sp<AMessage> *response) {
+ status_t err = msg->postAndAwaitResponse(response);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (!(*response)->findInt32("err", &err)) {
+ err = OK;
+ }
+
+ return err;
+}
+
+//static
+void FrameCaptureProcessor::PostReplyWithError(
+ const sp<AReplyToken> &replyID, status_t err) {
+ sp<AMessage> response = new AMessage;
+ if (err != OK) {
+ response->setInt32("err", err);
+ }
+ response->postReply(replyID);
+}
+
+FrameCaptureProcessor::FrameCaptureProcessor()
+ : mInitStatus(NO_INIT), mTextureName(0) {}
+
+FrameCaptureProcessor::~FrameCaptureProcessor() {
+ if (mLooper != nullptr) {
+ mLooper->unregisterHandler(id());
+ mLooper->stop();
+ }
+}
+
+void FrameCaptureProcessor::createRenderEngine() {
+ // this method should only be called once, immediately after ctor
+ CHECK(mInitStatus == NO_INIT);
+
+ mLooper = new ALooper();
+ mLooper->setName("capture_looper");
+ mLooper->start(); // default priority
+ mLooper->registerHandler(this);
+
+ sp<AMessage> response;
+ status_t err = PostAndAwaitResponse(new AMessage(kWhatCreate, this), &response);
+ if (err != OK) {
+ mInitStatus = ERROR_UNSUPPORTED;
+
+ mLooper->unregisterHandler(id());
+ mLooper->stop();
+ mLooper.clear();
+ return;
+ }
+
+ // only need one texture name
+ mRE->genTextures(1, &mTextureName);
+
+ mInitStatus = OK;
+}
+
+status_t FrameCaptureProcessor::capture(
+ const sp<Layer> &layer, const Rect &sourceCrop, const sp<GraphicBuffer> &buffer) {
+ sp<AMessage> msg = new AMessage(kWhatCapture, this);
+ msg->setObject("layer", layer);
+ msg->setRect("crop", sourceCrop.left, sourceCrop.top, sourceCrop.right, sourceCrop.bottom);
+ msg->setObject("buffer", buffer);
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
+status_t FrameCaptureProcessor::onCreate() {
+ mRE = renderengine::RenderEngine::create(
+ renderengine::RenderEngineCreationArgs::Builder()
+ .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
+ .setImageCacheSize(2 /*maxFrameBufferAcquiredBuffers*/)
+ .setUseColorManagerment(true)
+ .setEnableProtectedContext(false)
+ .setPrecacheToneMapperShaderOnly(true)
+ .setContextPriority(renderengine::RenderEngine::ContextPriority::LOW)
+ .build());
+
+ if (mRE == nullptr) {
+ return ERROR_UNSUPPORTED;
+ }
+ return OK;
+}
+
+status_t FrameCaptureProcessor::onCapture(const sp<Layer> &layer,
+ const Rect &sourceCrop, const sp<GraphicBuffer> &buffer) {
+ renderengine::DisplaySettings clientCompositionDisplay;
+ std::vector<renderengine::LayerSettings> clientCompositionLayers;
+
+ clientCompositionDisplay.physicalDisplay = sourceCrop;
+ clientCompositionDisplay.clip = sourceCrop;
+
+ clientCompositionDisplay.outputDataspace = ui::Dataspace::V0_SRGB;
+ clientCompositionDisplay.maxLuminance = sDefaultMaxLumiance;
+ clientCompositionDisplay.clearRegion = Region::INVALID_REGION;
+
+ // from Layer && BufferLayer
+ renderengine::LayerSettings layerSettings;
+
+ layer->getLayerSettings(sourceCrop, mTextureName, &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.
+ base::unique_fd bufferFence;
+ base::unique_fd drawFence;
+ mRE->useProtectedContext(false);
+ status_t err = mRE->drawLayers(clientCompositionDisplay, clientCompositionLayers, buffer.get(),
+ /*useFramebufferCache=*/false, std::move(bufferFence), &drawFence);
+
+ sp<Fence> fence = new Fence(std::move(drawFence));
+
+ if (err != OK) {
+ ALOGE("drawLayers returned err %d", err);
+ return err;
+ }
+
+ err = fence->wait(500);
+ if (err != OK) {
+ ALOGW("wait for fence returned err %d", err);
+ }
+ return OK;
+}
+
+void FrameCaptureProcessor::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatCreate:
+ {
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ status_t err = onCreate();
+
+ PostReplyWithError(replyID, err);
+ break;
+ }
+ case kWhatCapture:
+ {
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ sp<RefBase> layerObj, bufferObj;
+ int32_t left, top, right, bottom;
+ CHECK(msg->findObject("layer", &layerObj));
+ CHECK(msg->findRect("crop", &left, &top, &right, &bottom));
+ CHECK(msg->findObject("buffer", &bufferObj));
+
+ sp<GraphicBuffer> buffer = static_cast<GraphicBuffer*>(bufferObj.get());
+ sp<Layer> layer = static_cast<Layer*>(layerObj.get());
+
+ PostReplyWithError(replyID,
+ onCapture(layer, Rect(left, top, right, bottom), buffer));
+
+ break;
+ }
+ default:
+ TRESPASS();
+ }
+}
+
+} // namespace android
diff --git a/media/libstagefright/FrameDecoder.cpp b/media/libstagefright/FrameDecoder.cpp
index 08f690b..d75b317 100644
--- a/media/libstagefright/FrameDecoder.cpp
+++ b/media/libstagefright/FrameDecoder.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "FrameDecoder"
#include "include/FrameDecoder.h"
+#include "include/FrameCaptureLayer.h"
#include <binder/MemoryBase.h>
#include <binder/MemoryHeapBase.h>
#include <gui/Surface.h>
@@ -28,7 +29,9 @@
#include <media/stagefright/foundation/avc_utils.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/ColorUtils.h>
#include <media/stagefright/ColorConverter.h>
+#include <media/stagefright/FrameCaptureProcessor.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/MediaDefs.h>
@@ -41,10 +44,11 @@
static const int64_t kBufferTimeOutUs = 10000LL; // 10 msec
static const size_t kRetryCount = 50; // must be >0
+static const int64_t kDefaultSampleDurationUs = 33333LL; // 33ms
sp<IMemory> allocVideoFrame(const sp<MetaData>& trackMeta,
int32_t width, int32_t height, int32_t tileWidth, int32_t tileHeight,
- int32_t dstBpp, bool metaOnly = false) {
+ int32_t dstBpp, bool allocRotated, bool metaOnly) {
int32_t rotationAngle;
if (!trackMeta->findInt32(kKeyRotation, &rotationAngle)) {
rotationAngle = 0; // By default, no rotation
@@ -74,6 +78,14 @@
displayHeight = height;
}
+ if (allocRotated && (rotationAngle == 90 || rotationAngle == 270)) {
+ int32_t tmp;
+ tmp = width; width = height; height = tmp;
+ tmp = displayWidth; displayWidth = displayHeight; displayHeight = tmp;
+ tmp = tileWidth; tileWidth = tileHeight; tileHeight = tmp;
+ rotationAngle = 0;
+ }
+
VideoFrame frame(width, height, displayWidth, displayHeight,
tileWidth, tileHeight, rotationAngle, dstBpp, !metaOnly, iccSize);
@@ -88,12 +100,26 @@
ALOGE("not enough memory for VideoFrame size=%zu", size);
return NULL;
}
- VideoFrame* frameCopy = static_cast<VideoFrame*>(frameMem->pointer());
+ VideoFrame* frameCopy = static_cast<VideoFrame*>(frameMem->unsecurePointer());
frameCopy->init(frame, iccData, iccSize);
return frameMem;
}
+sp<IMemory> allocVideoFrame(const sp<MetaData>& trackMeta,
+ int32_t width, int32_t height, int32_t tileWidth, int32_t tileHeight,
+ int32_t dstBpp, bool allocRotated = false) {
+ return allocVideoFrame(trackMeta, width, height, tileWidth, tileHeight, dstBpp,
+ allocRotated, false /*metaOnly*/);
+}
+
+sp<IMemory> allocMetaFrame(const sp<MetaData>& trackMeta,
+ int32_t width, int32_t height, int32_t tileWidth, int32_t tileHeight,
+ int32_t dstBpp) {
+ return allocVideoFrame(trackMeta, width, height, tileWidth, tileHeight, dstBpp,
+ false /*allocRotated*/, true /*metaOnly*/);
+}
+
bool findThumbnailInfo(
const sp<MetaData> &trackMeta, int32_t *width, int32_t *height,
uint32_t *type = NULL, const void **data = NULL, size_t *size = NULL) {
@@ -117,23 +143,27 @@
bool getDstColorFormat(
android_pixel_format_t colorFormat,
OMX_COLOR_FORMATTYPE *dstFormat,
+ ui::PixelFormat *captureFormat,
int32_t *dstBpp) {
switch (colorFormat) {
case HAL_PIXEL_FORMAT_RGB_565:
{
*dstFormat = OMX_COLOR_Format16bitRGB565;
+ *captureFormat = ui::PixelFormat::RGB_565;
*dstBpp = 2;
return true;
}
case HAL_PIXEL_FORMAT_RGBA_8888:
{
*dstFormat = OMX_COLOR_Format32BitRGBA8888;
+ *captureFormat = ui::PixelFormat::RGBA_8888;
*dstBpp = 4;
return true;
}
case HAL_PIXEL_FORMAT_BGRA_8888:
{
*dstFormat = OMX_COLOR_Format32bitBGRA8888;
+ *captureFormat = ui::PixelFormat::BGRA_8888;
*dstBpp = 4;
return true;
}
@@ -150,9 +180,10 @@
sp<IMemory> FrameDecoder::getMetadataOnly(
const sp<MetaData> &trackMeta, int colorFormat, bool thumbnail) {
OMX_COLOR_FORMATTYPE dstFormat;
+ ui::PixelFormat captureFormat;
int32_t dstBpp;
- if (!getDstColorFormat(
- (android_pixel_format_t)colorFormat, &dstFormat, &dstBpp)) {
+ if (!getDstColorFormat((android_pixel_format_t)colorFormat,
+ &dstFormat, &captureFormat, &dstBpp)) {
return NULL;
}
@@ -170,8 +201,19 @@
tileWidth = tileHeight = 0;
}
}
- return allocVideoFrame(trackMeta,
- width, height, tileWidth, tileHeight, dstBpp, true /*metaOnly*/);
+
+ sp<IMemory> metaMem = allocMetaFrame(trackMeta, width, height, tileWidth, tileHeight, dstBpp);
+
+ // try to fill sequence meta's duration based on average frame rate,
+ // default to 33ms if frame rate is unavailable.
+ int32_t frameRate;
+ VideoFrame* meta = static_cast<VideoFrame*>(metaMem->unsecurePointer());
+ if (trackMeta->findInt32(kKeyFrameRate, &frameRate) && frameRate > 0) {
+ meta->mDurationUs = 1000000LL / frameRate;
+ } else {
+ meta->mDurationUs = kDefaultSampleDurationUs;
+ }
+ return metaMem;
}
FrameDecoder::FrameDecoder(
@@ -194,15 +236,30 @@
}
}
+bool isHDR(const sp<AMessage> &format) {
+ uint32_t standard, range, transfer;
+ if (!format->findInt32("color-standard", (int32_t*)&standard)) {
+ standard = 0;
+ }
+ if (!format->findInt32("color-range", (int32_t*)&range)) {
+ range = 0;
+ }
+ if (!format->findInt32("color-transfer", (int32_t*)&transfer)) {
+ transfer = 0;
+ }
+ return standard == ColorUtils::kColorStandardBT2020 &&
+ transfer == ColorUtils::kColorTransferST2084;
+}
+
status_t FrameDecoder::init(
- int64_t frameTimeUs, size_t numFrames, int option, int colorFormat) {
- if (!getDstColorFormat(
- (android_pixel_format_t)colorFormat, &mDstFormat, &mDstBpp)) {
+ int64_t frameTimeUs, int option, int colorFormat) {
+ if (!getDstColorFormat((android_pixel_format_t)colorFormat,
+ &mDstFormat, &mCaptureFormat, &mDstBpp)) {
return ERROR_UNSUPPORTED;
}
sp<AMessage> videoFormat = onGetFormatAndSeekOptions(
- frameTimeUs, numFrames, option, &mReadOptions);
+ frameTimeUs, option, &mReadOptions, &mSurface);
if (videoFormat == NULL) {
ALOGE("video format or seek mode not supported");
return ERROR_UNSUPPORTED;
@@ -219,7 +276,7 @@
}
err = decoder->configure(
- videoFormat, NULL /* surface */, NULL /* crypto */, 0 /* flags */);
+ videoFormat, mSurface, NULL /* crypto */, 0 /* flags */);
if (err != OK) {
ALOGW("configure returned error %d (%s)", err, asString(err));
decoder->release();
@@ -253,19 +310,7 @@
return NULL;
}
- return mFrames.size() > 0 ? mFrames[0] : NULL;
-}
-
-status_t FrameDecoder::extractFrames(std::vector<sp<IMemory> >* frames) {
- status_t err = extractInternal();
- if (err != OK) {
- return err;
- }
-
- for (size_t i = 0; i < mFrames.size(); i++) {
- frames->push_back(mFrames[i]);
- }
- return OK;
+ return mFrameMemory;
}
status_t FrameDecoder::extractInternal() {
@@ -379,8 +424,13 @@
ALOGE("failed to get output buffer %zu", index);
break;
}
- err = onOutputReceived(videoFrameBuffer, mOutputFormat, ptsUs, &done);
- mDecoder->releaseOutputBuffer(index);
+ if (mSurface != nullptr) {
+ mDecoder->renderOutputBufferAndRelease(index);
+ err = onOutputReceived(videoFrameBuffer, mOutputFormat, ptsUs, &done);
+ } else {
+ err = onOutputReceived(videoFrameBuffer, mOutputFormat, ptsUs, &done);
+ mDecoder->releaseOutputBuffer(index);
+ }
} else {
ALOGW("Received error %d (%s) instead of output", err, asString(err));
done = true;
@@ -404,22 +454,23 @@
const sp<MetaData> &trackMeta,
const sp<IMediaSource> &source)
: FrameDecoder(componentName, trackMeta, source),
+ mFrame(NULL),
mIsAvcOrHevc(false),
mSeekMode(MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC),
mTargetTimeUs(-1LL),
- mNumFrames(0),
- mNumFramesDecoded(0) {
+ mDefaultSampleDurationUs(0) {
}
sp<AMessage> VideoFrameDecoder::onGetFormatAndSeekOptions(
- int64_t frameTimeUs, size_t numFrames, int seekMode, MediaSource::ReadOptions *options) {
+ int64_t frameTimeUs, int seekMode,
+ MediaSource::ReadOptions *options,
+ sp<Surface> *window) {
mSeekMode = static_cast<MediaSource::ReadOptions::SeekMode>(seekMode);
if (mSeekMode < MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC ||
mSeekMode > MediaSource::ReadOptions::SEEK_FRAME_INDEX) {
ALOGE("Unknown seek mode: %d", mSeekMode);
return NULL;
}
- mNumFrames = numFrames;
const char *mime;
if (!trackMeta()->findCString(kKeyMIMEType, &mime)) {
@@ -460,6 +511,23 @@
videoFormat->setInt32("android._num-input-buffers", 1);
videoFormat->setInt32("android._num-output-buffers", 1);
}
+
+ if (isHDR(videoFormat)) {
+ *window = initSurface();
+ if (*window == NULL) {
+ ALOGE("Failed to init surface control for HDR, fallback to non-hdr");
+ } else {
+ videoFormat->setInt32("color-format", OMX_COLOR_FormatAndroidOpaque);
+ }
+ }
+
+ int32_t frameRate;
+ if (trackMeta()->findInt32(kKeyFrameRate, &frameRate) && frameRate > 0) {
+ mDefaultSampleDurationUs = 1000000LL / frameRate;
+ } else {
+ mDefaultSampleDurationUs = kDefaultSampleDurationUs;
+ }
+
return videoFormat;
}
@@ -480,6 +548,12 @@
// option, in which case we need to actually decode to targetTimeUs.
*flags |= MediaCodec::BUFFER_FLAG_EOS;
}
+ int64_t durationUs;
+ if (sampleMeta.findInt64(kKeyDuration, &durationUs)) {
+ mSampleDurations.push_back(durationUs);
+ } else {
+ mSampleDurations.push_back(mDefaultSampleDurationUs);
+ }
return OK;
}
@@ -487,6 +561,11 @@
const sp<MediaCodecBuffer> &videoFrameBuffer,
const sp<AMessage> &outputFormat,
int64_t timeUs, bool *done) {
+ int64_t durationUs = mDefaultSampleDurationUs;
+ if (!mSampleDurations.empty()) {
+ durationUs = *mSampleDurations.begin();
+ mSampleDurations.erase(mSampleDurations.begin());
+ }
bool shouldOutput = (mTargetTimeUs < 0LL) || (timeUs >= mTargetTimeUs);
// If this is not the target frame, skip color convert.
@@ -495,7 +574,7 @@
return OK;
}
- *done = (++mNumFramesDecoded >= mNumFrames);
+ *done = true;
if (outputFormat == NULL) {
return ERROR_MALFORMED;
@@ -504,13 +583,22 @@
int32_t width, height, stride, srcFormat;
if (!outputFormat->findInt32("width", &width) ||
!outputFormat->findInt32("height", &height) ||
- !outputFormat->findInt32("stride", &stride) ||
!outputFormat->findInt32("color-format", &srcFormat)) {
ALOGE("format missing dimension or color: %s",
outputFormat->debugString().c_str());
return ERROR_MALFORMED;
}
+ if (!outputFormat->findInt32("stride", &stride)) {
+ if (mCaptureLayer == NULL) {
+ ALOGE("format must have stride for byte buffer mode: %s",
+ outputFormat->debugString().c_str());
+ return ERROR_MALFORMED;
+ }
+ // for surface output, set stride to width, we don't actually need it.
+ stride = width;
+ }
+
int32_t crop_left, crop_top, crop_right, crop_bottom;
if (!outputFormat->findRect("crop", &crop_left, &crop_top, &crop_right, &crop_bottom)) {
crop_left = crop_top = 0;
@@ -518,15 +606,25 @@
crop_bottom = height - 1;
}
- sp<IMemory> frameMem = allocVideoFrame(
- trackMeta(),
- (crop_right - crop_left + 1),
- (crop_bottom - crop_top + 1),
- 0,
- 0,
- dstBpp());
- addFrame(frameMem);
- VideoFrame* frame = static_cast<VideoFrame*>(frameMem->pointer());
+ if (mFrame == NULL) {
+ sp<IMemory> frameMem = allocVideoFrame(
+ trackMeta(),
+ (crop_right - crop_left + 1),
+ (crop_bottom - crop_top + 1),
+ 0,
+ 0,
+ dstBpp(),
+ mCaptureLayer != nullptr /*allocRotated*/);
+ mFrame = static_cast<VideoFrame*>(frameMem->unsecurePointer());
+
+ setFrame(frameMem);
+ }
+
+ mFrame->mDurationUs = durationUs;
+
+ if (mCaptureLayer != nullptr) {
+ return captureSurface();
+ }
ColorConverter converter((OMX_COLOR_FORMATTYPE)srcFormat, dstFormat());
@@ -547,8 +645,8 @@
(const uint8_t *)videoFrameBuffer->data(),
width, height, stride,
crop_left, crop_top, crop_right, crop_bottom,
- frame->getFlattenedData(),
- frame->mWidth, frame->mHeight, frame->mRowBytes,
+ mFrame->getFlattenedData(),
+ mFrame->mWidth, mFrame->mHeight, mFrame->mRowBytes,
crop_left, crop_top, crop_right, crop_bottom);
return OK;
}
@@ -558,6 +656,57 @@
return ERROR_UNSUPPORTED;
}
+sp<Surface> VideoFrameDecoder::initSurface() {
+ // create the consumer listener interface, and hold sp so that this
+ // interface lives as long as the GraphicBufferSource.
+ sp<FrameCaptureLayer> captureLayer = new FrameCaptureLayer();
+ if (captureLayer->init() != OK) {
+ ALOGE("failed to init capture layer");
+ return nullptr;
+ }
+ mCaptureLayer = captureLayer;
+
+ return captureLayer->getSurface();
+}
+
+status_t VideoFrameDecoder::captureSurface() {
+ sp<GraphicBuffer> outBuffer;
+ status_t err = mCaptureLayer->capture(
+ captureFormat(), Rect(0, 0, mFrame->mWidth, mFrame->mHeight), &outBuffer);
+
+ if (err != OK) {
+ ALOGE("failed to capture layer (err %d)", err);
+ return err;
+ }
+
+ ALOGV("capture: %dx%d, format %d, stride %d",
+ outBuffer->getWidth(),
+ outBuffer->getHeight(),
+ outBuffer->getPixelFormat(),
+ outBuffer->getStride());
+
+ uint8_t *base;
+ int32_t outBytesPerPixel, outBytesPerStride;
+ err = outBuffer->lock(
+ GraphicBuffer::USAGE_SW_READ_OFTEN,
+ reinterpret_cast<void**>(&base),
+ &outBytesPerPixel,
+ &outBytesPerStride);
+ if (err != OK) {
+ ALOGE("failed to lock graphic buffer: err %d", err);
+ return err;
+ }
+
+ uint8_t *dst = mFrame->getFlattenedData();
+ for (size_t y = 0 ; y < fmin(mFrame->mHeight, outBuffer->getHeight()) ; y++) {
+ memcpy(dst, base, fmin(mFrame->mWidth, outBuffer->getWidth()) * mFrame->mBytesPerPixel);
+ dst += mFrame->mRowBytes;
+ base += outBuffer->getStride() * mFrame->mBytesPerPixel;
+ }
+ outBuffer->unlock();
+ return OK;
+}
+
////////////////////////////////////////////////////////////////////////
ImageDecoder::ImageDecoder(
@@ -577,8 +726,8 @@
}
sp<AMessage> ImageDecoder::onGetFormatAndSeekOptions(
- int64_t frameTimeUs, size_t /*numFrames*/,
- int /*seekMode*/, MediaSource::ReadOptions *options) {
+ int64_t frameTimeUs, int /*seekMode*/,
+ MediaSource::ReadOptions *options, sp<Surface> * /*window*/) {
sp<MetaData> overrideMeta;
if (frameTimeUs < 0) {
uint32_t type;
@@ -703,9 +852,9 @@
if (mFrame == NULL) {
sp<IMemory> frameMem = allocVideoFrame(
trackMeta(), mWidth, mHeight, mTileWidth, mTileHeight, dstBpp());
- mFrame = static_cast<VideoFrame*>(frameMem->pointer());
+ mFrame = static_cast<VideoFrame*>(frameMem->unsecurePointer());
- addFrame(frameMem);
+ setFrame(frameMem);
}
int32_t srcFormat;
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 f130c9b..c88a82a 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();
@@ -736,9 +748,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 +792,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 +802,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 +846,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 +912,8 @@
if (mInMemoryCacheSize == 0) {
int32_t bitRate = -1;
if (mHasFileLevelMeta) {
- mInMemoryCacheSize += estimateFileLevelMetaSize(param);
+ mFileLevelMetaDataSize = estimateFileLevelMetaSize(param);
+ mInMemoryCacheSize += mFileLevelMetaDataSize;
}
if (mHasMoovBox) {
if (param) {
@@ -926,7 +924,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 +933,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 +952,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 +1031,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 +1076,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 +1098,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 +1117,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 +1137,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 +1206,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 +1280,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 +1383,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 +1436,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 +1478,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 +1490,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 +1546,7 @@
}
void MPEG4Writer::beginBox(const char *fourcc) {
+ ALOGV("beginBox:%s", fourcc);
CHECK_EQ(strlen(fourcc), 4u);
mBoxes.push_back(mWriteBoxToMemory?
@@ -1522,10 +1566,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 +1724,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 +1882,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 +1913,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 +1927,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 +1995,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 +2027,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 +2059,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 +2085,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 +2149,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 +2165,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 +2221,7 @@
if (hasGrid) {
mDimgRefs.value.push_back(mOwner->addItem_l({
.itemType = "hvc1",
+ .itemId = mItemIdBase++,
.isPrimary = false,
.isHidden = true,
.offset = (uint32_t)offset,
@@ -2114,6 +2244,7 @@
}
mImageItemId = mOwner->addItem_l({
.itemType = "grid",
+ .itemId = mItemIdBase++,
.isPrimary = (mIsPrimary != 0),
.isHidden = false,
.rows = (uint32_t)mGridRows,
@@ -2126,6 +2257,7 @@
} else {
mImageItemId = mOwner->addItem_l({
.itemType = "hvc1",
+ .itemId = mItemIdBase++,
.isPrimary = (mIsPrimary != 0),
.isHidden = false,
.offset = (uint32_t)offset,
@@ -2195,6 +2327,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();
}
@@ -2213,8 +2361,10 @@
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC) ||
!strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC)) {
mMeta->findData(kKeyHVCC, &type, &data, &size);
- } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)
- || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION)) {
+ mMeta->findData(kKeyDVCC, &type, &data, &size);
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) ||
+ !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
if (mMeta->findData(kKeyESDS, &type, &data, &size)) {
ESDS esds(data, size);
if (esds.getCodecSpecificInfo(&data, &size) == OK &&
@@ -2234,7 +2384,6 @@
stop();
delete mStszTableEntries;
- delete mStcoTableEntries;
delete mCo64TableEntries;
delete mStscTableEntries;
delete mSttsTableEntries;
@@ -2243,7 +2392,6 @@
delete mElstTableEntries;
mStszTableEntries = NULL;
- mStcoTableEntries = NULL;
mCo64TableEntries = NULL;
mStscTableEntries = NULL;
mSttsTableEntries = NULL;
@@ -2383,7 +2531,6 @@
if (interChunkTimeUs > it->mPrevChunkTimestampUs) {
it->mMaxInterChunkDurUs = interChunkTimeUs;
}
-
return true;
}
}
@@ -2466,6 +2613,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);
@@ -2542,10 +2705,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;
}
@@ -2957,11 +3121,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()) {
@@ -2981,6 +3145,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.
@@ -2991,7 +3156,6 @@
}
++count;
-
int32_t isCodecConfig;
if (buffer->meta_data().findInt32(kKeyIsCodecConfig, &isCodecConfig)
&& isCodecConfig) {
@@ -3058,6 +3222,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
@@ -3069,7 +3245,6 @@
meta_data = new MetaData(buffer->meta_data());
buffer->release();
buffer = NULL;
-
if (isExif) {
copy->meta_data().setInt32(kKeyExifTiffOffset, tiffHdrOffset);
}
@@ -3117,10 +3292,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) {
@@ -3132,10 +3307,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;
@@ -3307,17 +3485,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) {
@@ -3350,11 +3528,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);
}
}
@@ -3390,9 +3564,7 @@
}
}
}
-
}
-
if (isTrackMalFormed()) {
dumpTimeStamps();
err = ERROR_MALFORMED;
@@ -3424,17 +3596,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);
@@ -3685,7 +3850,7 @@
"Metadata";
}
-void MPEG4Writer::Track::writeTrackHeader(bool use32BitOffset) {
+void MPEG4Writer::Track::writeTrackHeader() {
uint32_t now = getMpeg4Time();
mOwner->beginBox("trak");
writeTkhdBox(now);
@@ -3702,7 +3867,7 @@
writeNmhdBox();
}
writeDinfBox();
- writeStblBox(use32BitOffset);
+ writeStblBox();
mOwner->endBox(); // minf
mOwner->endBox(); // mdia
mOwner->endBox(); // trak
@@ -3718,7 +3883,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
@@ -3738,7 +3903,7 @@
}
writeStszBox();
writeStscBox();
- writeStcoBox(use32BitOffset);
+ writeCo64Box();
mOwner->endBox(); // stbl
}
@@ -4097,19 +4262,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) {
@@ -4251,19 +4519,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
}
@@ -4284,7 +4539,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
@@ -4325,14 +4582,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
}
@@ -4455,9 +4708,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
@@ -4468,8 +4723,8 @@
writeInt32(mNumGrids++ * 8);
writeInt32(8);
} else {
- writeInt32(mItems[i].offset);
- writeInt32(mItems[i].size);
+ writeInt32(item.offset);
+ writeInt32(item.size);
}
}
endBox();
@@ -4498,9 +4753,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();
@@ -4509,20 +4766,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);
}
}
}
@@ -4534,11 +4793,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);
@@ -4613,12 +4874,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) {
@@ -4648,19 +4911,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++;
}
}
@@ -4714,15 +4979,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()) {
@@ -4733,24 +5008,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 77eace9..712be41 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -21,25 +21,25 @@
#include <inttypes.h>
#include <stdlib.h>
-#include "include/SecureBuffer.h"
-#include "include/SharedMemoryBuffer.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>
+#include <aidl/android/media/BnResourceManagerClient.h>
+#include <aidl/android/media/IResourceManagerService.h>
+#include <android/binder_ibinder.h>
+#include <android/binder_manager.h>
#include <binder/IMemory.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
#include <binder/MemoryDealer.h>
#include <cutils/properties.h>
#include <gui/BufferQueue.h>
#include <gui/Surface.h>
#include <mediadrm/ICrypto.h>
#include <media/IOMX.h>
-#include <media/IResourceManagerService.h>
#include <media/MediaCodecBuffer.h>
-#include <media/MediaAnalyticsItem.h>
+#include <media/MediaMetricsItem.h>
+#include <media/MediaResource.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
@@ -50,6 +50,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>
@@ -63,6 +64,11 @@
namespace android {
+using Status = ::ndk::ScopedAStatus;
+using aidl::android::media::BnResourceManagerClient;
+using aidl::android::media::IResourceManagerClient;
+using aidl::android::media::IResourceManagerService;
+
// key for media statistics
static const char *kCodecKeyName = "codec";
// attrs for media statistics
@@ -95,6 +101,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 */
@@ -106,7 +116,7 @@
static bool kEmitHistogram = false;
-static int64_t getId(const sp<IResourceManagerClient> &client) {
+static int64_t getId(const std::shared_ptr<IResourceManagerClient> &client) {
return (int64_t) client.get();
}
@@ -123,11 +133,12 @@
struct ResourceManagerClient : public BnResourceManagerClient {
explicit ResourceManagerClient(MediaCodec* codec) : mMediaCodec(codec) {}
- virtual bool reclaimResource() {
+ Status reclaimResource(bool* _aidl_return) override {
sp<MediaCodec> codec = mMediaCodec.promote();
if (codec == NULL) {
// codec is already gone.
- return true;
+ *_aidl_return = true;
+ return Status::ok();
}
status_t err = codec->reclaim();
if (err == WOULD_BLOCK) {
@@ -139,25 +150,25 @@
if (err != OK) {
ALOGW("ResourceManagerClient failed to release codec with err %d", err);
}
- return (err == OK);
+ *_aidl_return = (err == OK);
+ return Status::ok();
}
- virtual String8 getName() {
- String8 ret;
+ Status getName(::std::string* _aidl_return) override {
+ _aidl_return->clear();
sp<MediaCodec> codec = mMediaCodec.promote();
if (codec == NULL) {
// codec is already gone.
- return ret;
+ return Status::ok();
}
AString name;
if (codec->getName(&name) == OK) {
- ret.setTo(name.c_str());
+ *_aidl_return = name.c_str();
}
- return ret;
+ return Status::ok();
}
-protected:
virtual ~ResourceManagerClient() {}
private:
@@ -166,73 +177,110 @@
DISALLOW_EVIL_CONSTRUCTORS(ResourceManagerClient);
};
+struct MediaCodec::ResourceManagerServiceProxy : public RefBase {
+ ResourceManagerServiceProxy(pid_t pid, uid_t uid,
+ const std::shared_ptr<IResourceManagerClient> &client);
+ virtual ~ResourceManagerServiceProxy();
+
+ void init();
+
+ // implements DeathRecipient
+ static void BinderDiedCallback(void* cookie);
+ void binderDied();
+
+ void addResource(const MediaResourceParcel &resource);
+ void removeResource(const MediaResourceParcel &resource);
+ void removeClient();
+ bool reclaimResource(const std::vector<MediaResourceParcel> &resources);
+
+private:
+ Mutex mLock;
+ pid_t mPid;
+ uid_t mUid;
+ std::shared_ptr<IResourceManagerService> mService;
+ std::shared_ptr<IResourceManagerClient> mClient;
+ ::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
+};
+
MediaCodec::ResourceManagerServiceProxy::ResourceManagerServiceProxy(
- pid_t pid, uid_t uid)
- : mPid(pid), mUid(uid) {
+ pid_t pid, uid_t uid, const std::shared_ptr<IResourceManagerClient> &client)
+ : mPid(pid), mUid(uid), mClient(client),
+ mDeathRecipient(AIBinder_DeathRecipient_new(BinderDiedCallback)) {
if (mPid == MediaCodec::kNoPid) {
- mPid = IPCThreadState::self()->getCallingPid();
+ mPid = AIBinder_getCallingPid();
}
}
MediaCodec::ResourceManagerServiceProxy::~ResourceManagerServiceProxy() {
- if (mService != NULL) {
- IInterface::asBinder(mService)->unlinkToDeath(this);
+ if (mService != nullptr) {
+ AIBinder_unlinkToDeath(mService->asBinder().get(), mDeathRecipient.get(), this);
}
}
void MediaCodec::ResourceManagerServiceProxy::init() {
- sp<IServiceManager> sm = defaultServiceManager();
- sp<IBinder> binder = sm->getService(String16("media.resource_manager"));
- mService = interface_cast<IResourceManagerService>(binder);
- if (mService == NULL) {
+ ::ndk::SpAIBinder binder(AServiceManager_getService("media.resource_manager"));
+ mService = IResourceManagerService::fromBinder(binder);
+ if (mService == nullptr) {
ALOGE("Failed to get ResourceManagerService");
return;
}
- IInterface::asBinder(mService)->linkToDeath(this);
+
+ AIBinder_linkToDeath(mService->asBinder().get(), mDeathRecipient.get(), this);
}
-void MediaCodec::ResourceManagerServiceProxy::binderDied(const wp<IBinder>& /*who*/) {
+//static
+void MediaCodec::ResourceManagerServiceProxy::BinderDiedCallback(void* cookie) {
+ auto thiz = static_cast<ResourceManagerServiceProxy*>(cookie);
+ thiz->binderDied();
+}
+
+void MediaCodec::ResourceManagerServiceProxy::binderDied() {
ALOGW("ResourceManagerService died.");
Mutex::Autolock _l(mLock);
- mService.clear();
+ mService = nullptr;
}
void MediaCodec::ResourceManagerServiceProxy::addResource(
- int64_t clientId,
- const sp<IResourceManagerClient> &client,
- const Vector<MediaResource> &resources) {
+ const MediaResourceParcel &resource) {
+ std::vector<MediaResourceParcel> resources;
+ resources.push_back(resource);
+
Mutex::Autolock _l(mLock);
- if (mService == NULL) {
+ if (mService == nullptr) {
return;
}
- mService->addResource(mPid, mUid, clientId, client, resources);
+ mService->addResource(mPid, mUid, getId(mClient), mClient, resources);
}
void MediaCodec::ResourceManagerServiceProxy::removeResource(
- int64_t clientId,
- const Vector<MediaResource> &resources) {
+ const MediaResourceParcel &resource) {
+ std::vector<MediaResourceParcel> resources;
+ resources.push_back(resource);
+
Mutex::Autolock _l(mLock);
- if (mService == NULL) {
+ if (mService == nullptr) {
return;
}
- mService->removeResource(mPid, clientId, resources);
+ mService->removeResource(mPid, getId(mClient), resources);
}
-void MediaCodec::ResourceManagerServiceProxy::removeClient(int64_t clientId) {
+void MediaCodec::ResourceManagerServiceProxy::removeClient() {
Mutex::Autolock _l(mLock);
- if (mService == NULL) {
+ if (mService == nullptr) {
return;
}
- mService->removeClient(mPid, clientId);
+ mService->removeClient(mPid, getId(mClient));
}
bool MediaCodec::ResourceManagerServiceProxy::reclaimResource(
- const Vector<MediaResource> &resources) {
+ const std::vector<MediaResourceParcel> &resources) {
Mutex::Autolock _l(mLock);
if (mService == NULL) {
return false;
}
- return mService->reclaimResource(mPid, resources);
+ bool success;
+ Status status = mService->reclaimResource(mPid, resources, &success);
+ return status.isOk() && success;
}
////////////////////////////////////////////////////////////////////////////////
@@ -490,9 +538,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;
}
@@ -506,7 +552,7 @@
sp<IOMX> omx = client.interface();
sp<IGraphicBufferProducer> bufferProducer;
- sp<IGraphicBufferSource> bufferSource;
+ sp<hardware::media::omx::V1_0::IGraphicBufferSource> bufferSource;
status_t err = omx->createInputSurface(&bufferProducer, &bufferSource);
@@ -539,21 +585,26 @@
mHaveInputSurface(false),
mHavePendingInputBuffers(false),
mCpuBoostRequested(false),
- mLatencyUnknown(0) {
+ mLatencyUnknown(0),
+ mNumLowLatencyEnables(0),
+ mNumLowLatencyDisables(0),
+ mIsLowLatencyModeOn(false),
+ mIndexOfFirstFrameWhenLowLatencyOn(-1),
+ mInputBufferCounter(0) {
if (uid == kNoUid) {
- mUid = IPCThreadState::self()->getCallingUid();
+ mUid = AIBinder_getCallingUid();
} else {
mUid = uid;
}
- mResourceManagerClient = new ResourceManagerClient(this);
- mResourceManagerService = new ResourceManagerServiceProxy(pid, mUid);
+ mResourceManagerProxy = new ResourceManagerServiceProxy(pid, mUid,
+ ::ndk::SharedRefBase::make<ResourceManagerClient>(this));
initMediametrics();
}
MediaCodec::~MediaCodec() {
CHECK_EQ(mState, UNINITIALIZED);
- mResourceManagerService->removeClient(getId(mResourceManagerClient));
+ mResourceManagerProxy->removeClient();
flushMediametrics();
}
@@ -572,6 +623,16 @@
}
mRecentHead = 0;
}
+
+ {
+ Mutex::Autolock al(mLatencyLock);
+ mBuffersInFlight.clear();
+ mNumLowLatencyEnables = 0;
+ mNumLowLatencyDisables = 0;
+ mIsLowLatencyModeOn = false;
+ mIndexOfFirstFrameWhenLowLatencyOn = -1;
+ mInputBufferCounter = 0;
+ }
}
void MediaCodec::updateMediametrics() {
@@ -597,6 +658,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);
@@ -653,6 +721,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) {
@@ -754,7 +838,7 @@
if (mBatteryChecker != nullptr) {
mBatteryChecker->onCodecActivity([this] () {
- addResource(MediaResource::kBattery, MediaResource::kVideoCodec, 1);
+ mResourceManagerProxy->addResource(MediaResource::VideoBatteryResource());
});
}
@@ -769,6 +853,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;
}
}
@@ -794,7 +883,7 @@
if (mBatteryChecker != nullptr) {
mBatteryChecker->onCodecActivity([this] () {
- addResource(MediaResource::kBattery, MediaResource::kVideoCodec, 1);
+ mResourceManagerProxy->addResource(MediaResource::VideoBatteryResource());
});
}
@@ -878,7 +967,7 @@
}
static CodecBase *CreateCCodec() {
- return StagefrightPluginLoader::GetCCodecInstance()->createCodec();
+ return new CCodec;
}
//static
@@ -904,7 +993,7 @@
}
status_t MediaCodec::init(const AString &name) {
- mResourceManagerService->init();
+ mResourceManagerProxy->init();
// save init parameters for reset
mInitName = name;
@@ -917,37 +1006,41 @@
mCodecInfo.clear();
bool secureCodec = false;
- AString tmp = name;
- if (tmp.endsWith(".secure")) {
- secureCodec = true;
- tmp.erase(tmp.size() - 7, 7);
- }
- const sp<IMediaCodecList> mcl = MediaCodecList::getInstance();
- if (mcl == NULL) {
- mCodec = NULL; // remove the codec.
- return NO_INIT; // if called from Java should raise IOException
- }
- for (const AString &codecName : { name, tmp }) {
- ssize_t codecIdx = mcl->findCodecByName(codecName.c_str());
- if (codecIdx < 0) {
- continue;
+ const char *owner = "";
+ if (!name.startsWith("android.filter.")) {
+ AString tmp = name;
+ if (tmp.endsWith(".secure")) {
+ secureCodec = true;
+ tmp.erase(tmp.size() - 7, 7);
}
- mCodecInfo = mcl->getCodecInfo(codecIdx);
- Vector<AString> mediaTypes;
- mCodecInfo->getSupportedMediaTypes(&mediaTypes);
- for (size_t i = 0; i < mediaTypes.size(); i++) {
- if (mediaTypes[i].startsWith("video/")) {
- mIsVideo = true;
- break;
+ const sp<IMediaCodecList> mcl = MediaCodecList::getInstance();
+ if (mcl == NULL) {
+ mCodec = NULL; // remove the codec.
+ return NO_INIT; // if called from Java should raise IOException
+ }
+ for (const AString &codecName : { name, tmp }) {
+ ssize_t codecIdx = mcl->findCodecByName(codecName.c_str());
+ if (codecIdx < 0) {
+ continue;
}
+ mCodecInfo = mcl->getCodecInfo(codecIdx);
+ Vector<AString> mediaTypes;
+ mCodecInfo->getSupportedMediaTypes(&mediaTypes);
+ for (size_t i = 0; i < mediaTypes.size(); i++) {
+ if (mediaTypes[i].startsWith("video/")) {
+ mIsVideo = true;
+ break;
+ }
+ }
+ break;
}
- break;
- }
- if (mCodecInfo == nullptr) {
- return NAME_NOT_FOUND;
+ if (mCodecInfo == nullptr) {
+ return NAME_NOT_FOUND;
+ }
+ owner = mCodecInfo->getOwnerName();
}
- mCodec = GetCodecBase(name, mCodecInfo->getOwnerName());
+ mCodec = GetCodecBase(name, owner);
if (mCodec == NULL) {
return NAME_NOT_FOUND;
}
@@ -976,9 +1069,11 @@
new BufferCallback(new AMessage(kWhatCodecNotify, this))));
sp<AMessage> msg = new AMessage(kWhatInit, this);
- msg->setObject("codecInfo", mCodecInfo);
- // name may be different from mCodecInfo->getCodecName() if we stripped
- // ".secure"
+ if (mCodecInfo) {
+ msg->setObject("codecInfo", mCodecInfo);
+ // name may be different from mCodecInfo->getCodecName() if we stripped
+ // ".secure"
+ }
msg->setString("name", name);
if (mMetricsHandle != 0) {
@@ -992,16 +1087,12 @@
}
status_t err;
- Vector<MediaResource> resources;
- MediaResource::Type type =
- secureCodec ? MediaResource::kSecureCodec : MediaResource::kNonSecureCodec;
- MediaResource::SubType subtype =
- mIsVideo ? MediaResource::kVideoCodec : MediaResource::kAudioCodec;
- resources.push_back(MediaResource(type, subtype, 1));
+ std::vector<MediaResourceParcel> resources;
+ resources.push_back(MediaResource::CodecResource(secureCodec, mIsVideo));
for (int i = 0; i <= kMaxRetry; ++i) {
if (i > 0) {
// Don't try to reclaim resource for the first time.
- if (!mResourceManagerService->reclaimResource(resources)) {
+ if (!mResourceManagerProxy->reclaimResource(resources)) {
break;
}
}
@@ -1087,6 +1178,8 @@
}
}
+ updateLowLatency(format);
+
msg->setMessage("format", format);
msg->setInt32("flags", flags);
msg->setObject("surface", surface);
@@ -1108,19 +1201,15 @@
mConfigureMsg = msg;
status_t err;
- Vector<MediaResource> resources;
- MediaResource::Type type = (mFlags & kFlagIsSecure) ?
- MediaResource::kSecureCodec : MediaResource::kNonSecureCodec;
- MediaResource::SubType subtype =
- mIsVideo ? MediaResource::kVideoCodec : MediaResource::kAudioCodec;
- resources.push_back(MediaResource(type, subtype, 1));
+ std::vector<MediaResourceParcel> resources;
+ resources.push_back(MediaResource::CodecResource(mFlags & kFlagIsSecure, mIsVideo));
// Don't know the buffer size at this point, but it's fine to use 1 because
// the reclaimResource call doesn't consider the requester's buffer size for now.
- resources.push_back(MediaResource(MediaResource::kGraphicMemory, 1));
+ resources.push_back(MediaResource::GraphicMemoryResource(1));
for (int i = 0; i <= kMaxRetry; ++i) {
if (i > 0) {
// Don't try to reclaim resource for the first time.
- if (!mResourceManagerService->reclaimResource(resources)) {
+ if (!mResourceManagerProxy->reclaimResource(resources)) {
break;
}
}
@@ -1241,38 +1330,19 @@
return size;
}
-void MediaCodec::addResource(
- MediaResource::Type type, MediaResource::SubType subtype, uint64_t value) {
- Vector<MediaResource> resources;
- resources.push_back(MediaResource(type, subtype, value));
- mResourceManagerService->addResource(
- getId(mResourceManagerClient), mResourceManagerClient, resources);
-}
-
-void MediaCodec::removeResource(
- MediaResource::Type type, MediaResource::SubType subtype, uint64_t value) {
- Vector<MediaResource> resources;
- resources.push_back(MediaResource(type, subtype, value));
- mResourceManagerService->removeResource(getId(mResourceManagerClient), resources);
-}
-
status_t MediaCodec::start() {
sp<AMessage> msg = new AMessage(kWhatStart, this);
status_t err;
- Vector<MediaResource> resources;
- MediaResource::Type type = (mFlags & kFlagIsSecure) ?
- MediaResource::kSecureCodec : MediaResource::kNonSecureCodec;
- MediaResource::SubType subtype =
- mIsVideo ? MediaResource::kVideoCodec : MediaResource::kAudioCodec;
- resources.push_back(MediaResource(type, subtype, 1));
+ std::vector<MediaResourceParcel> resources;
+ resources.push_back(MediaResource::CodecResource(mFlags & kFlagIsSecure, mIsVideo));
// Don't know the buffer size at this point, but it's fine to use 1 because
// the reclaimResource call doesn't consider the requester's buffer size for now.
- resources.push_back(MediaResource(MediaResource::kGraphicMemory, 1));
+ resources.push_back(MediaResource::GraphicMemoryResource(1));
for (int i = 0; i <= kMaxRetry; ++i) {
if (i > 0) {
// Don't try to reclaim resource for the first time.
- if (!mResourceManagerService->reclaimResource(resources)) {
+ if (!mResourceManagerProxy->reclaimResource(resources)) {
break;
}
// Recover codec from previous error before retry start.
@@ -1710,8 +1780,7 @@
totalPixel = width * height;
}
if (totalPixel >= 1920 * 1080) {
- addResource(MediaResource::kCpuBoost,
- MediaResource::kUnspecifiedSubType, 1);
+ mResourceManagerProxy->addResource(MediaResource::CpuBoostResource());
mCpuBoostRequested = true;
}
}
@@ -2047,29 +2116,27 @@
mComponentName.c_str());
}
- const char *owner = mCodecInfo->getOwnerName();
+ const char *owner = mCodecInfo ? mCodecInfo->getOwnerName() : "";
if (mComponentName.startsWith("OMX.google.")
- && (owner == nullptr || strncmp(owner, "default", 8) == 0)) {
+ && strncmp(owner, "default", 8) == 0) {
mFlags |= kFlagUsesSoftwareRenderer;
} else {
mFlags &= ~kFlagUsesSoftwareRenderer;
}
mOwnerName = owner;
- MediaResource::Type resourceType;
if (mComponentName.endsWith(".secure")) {
mFlags |= kFlagIsSecure;
- resourceType = MediaResource::kSecureCodec;
mediametrics_setInt32(mMetricsHandle, kCodecSecure, 1);
} else {
mFlags &= ~kFlagIsSecure;
- resourceType = MediaResource::kNonSecureCodec;
mediametrics_setInt32(mMetricsHandle, kCodecSecure, 0);
}
if (mIsVideo) {
// audio codec is currently ignored.
- addResource(resourceType, MediaResource::kVideoCodec, 1);
+ mResourceManagerProxy->addResource(
+ MediaResource::CodecResource(mFlags & kFlagIsSecure, mIsVideo));
}
(new AMessage)->postReply(mReplyID);
@@ -2190,10 +2257,8 @@
CHECK_EQ(mState, STARTING);
if (mIsVideo) {
- addResource(
- MediaResource::kGraphicMemory,
- MediaResource::kUnspecifiedSubType,
- getGraphicBufferSize());
+ mResourceManagerProxy->addResource(
+ MediaResource::GraphicMemoryResource(getGraphicBufferSize()));
}
setState(STARTED);
(new AMessage)->postReply(mReplyID);
@@ -2417,7 +2482,7 @@
mBatteryChecker->onClientRemoved();
}
- mResourceManagerService->removeClient(getId(mResourceManagerClient));
+ mResourceManagerProxy->removeClient();
(new AMessage)->postReply(mReplyID);
break;
@@ -2462,12 +2527,14 @@
setState(INITIALIZING);
sp<RefBase> codecInfo;
- CHECK(msg->findObject("codecInfo", &codecInfo));
+ (void)msg->findObject("codecInfo", &codecInfo);
AString name;
CHECK(msg->findString("name", &name));
sp<AMessage> format = new AMessage;
- format->setObject("codecInfo", codecInfo);
+ if (codecInfo) {
+ format->setObject("codecInfo", codecInfo);
+ }
format->setString("componentName", name);
mCodec->initiateAllocateComponent(format);
@@ -3132,7 +3199,8 @@
{
if (mBatteryChecker != nullptr) {
mBatteryChecker->onCheckBatteryTimer(msg, [this] () {
- removeResource(MediaResource::kBattery, MediaResource::kVideoCodec, 1);
+ mResourceManagerProxy->removeResource(
+ MediaResource::VideoBatteryResource());
});
}
break;
@@ -3693,6 +3761,7 @@
}
status_t MediaCodec::onSetParameters(const sp<AMessage> ¶ms) {
+ updateLowLatency(params);
mCodec->signalSetParameters(params);
return OK;
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/MediaCodecListOverrides.cpp b/media/libstagefright/MediaCodecListOverrides.cpp
index b027a97..4a167d1 100644
--- a/media/libstagefright/MediaCodecListOverrides.cpp
+++ b/media/libstagefright/MediaCodecListOverrides.cpp
@@ -264,7 +264,9 @@
}
}
}
- global_results->add(kPolicySupportsMultipleSecureCodecs, supportMultipleSecureCodecs);
+ global_results->add(
+ MediaResourcePolicy::kPolicySupportsMultipleSecureCodecs(),
+ supportMultipleSecureCodecs);
}
static AString globalResultsToXml(const CodecSettings &results) {
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/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp
index 120f354..2457561 100644
--- a/media/libstagefright/MediaExtractorFactory.cpp
+++ b/media/libstagefright/MediaExtractorFactory.cpp
@@ -27,8 +27,8 @@
#include <media/stagefright/InterfaceUtils.h>
#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/MediaExtractorFactory.h>
-#include <media/IMediaExtractor.h>
-#include <media/IMediaExtractorService.h>
+#include <android/IMediaExtractor.h>
+#include <android/IMediaExtractorService.h>
#include <nativeloader/dlext_namespaces.h>
#include <private/android_filesystem_config.h>
#include <cutils/properties.h>
@@ -54,9 +54,13 @@
sp<IBinder> binder = defaultServiceManager()->getService(String16("media.extractor"));
if (binder != 0) {
- sp<IMediaExtractorService> mediaExService(interface_cast<IMediaExtractorService>(binder));
- sp<IMediaExtractor> ex = mediaExService->makeExtractor(
- CreateIDataSourceFromDataSource(source), mime);
+ sp<IMediaExtractorService> mediaExService(
+ interface_cast<IMediaExtractorService>(binder));
+ sp<IMediaExtractor> ex;
+ mediaExService->makeExtractor(
+ CreateIDataSourceFromDataSource(source),
+ mime ? std::make_unique<std::string>(mime) : nullptr,
+ &ex);
return ex;
} else {
ALOGE("extractor service not running");
@@ -262,7 +266,7 @@
return strcmp(first->def.extractor_name, second->def.extractor_name) < 0;
}
-static std::unordered_set<std::string> gSupportedExtensions;
+static std::vector<std::string> gSupportedExtensions;
// static
void MediaExtractorFactory::LoadExtractors() {
@@ -308,7 +312,7 @@
if (ext == nullptr) {
break;
}
- gSupportedExtensions.insert(std::string(ext));
+ gSupportedExtensions.push_back(std::string(ext));
}
}
}
@@ -317,7 +321,7 @@
}
// static
-std::unordered_set<std::string> MediaExtractorFactory::getSupportedTypes() {
+std::vector<std::string> MediaExtractorFactory::getSupportedTypes() {
if (getuid() == AID_MEDIA_EX) {
return gSupportedExtensions;
}
@@ -326,9 +330,11 @@
if (binder != 0) {
sp<IMediaExtractorService> mediaExService(interface_cast<IMediaExtractorService>(binder));
- return mediaExService->getSupportedTypes();
+ std::vector<std::string> supportedTypes;
+ mediaExService->getSupportedTypes(&supportedTypes);
+ return supportedTypes;
}
- return std::unordered_set<std::string>();
+ return std::vector<std::string>();
}
status_t MediaExtractorFactory::dump(int fd, const Vector<String16>&) {
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/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..c841d10 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;
}
diff --git a/media/libstagefright/SimpleDecodingSource.cpp b/media/libstagefright/SimpleDecodingSource.cpp
index b809848..771dfea 100644
--- a/media/libstagefright/SimpleDecodingSource.cpp
+++ b/media/libstagefright/SimpleDecodingSource.cpp
@@ -36,7 +36,7 @@
using namespace android;
const int64_t kTimeoutWaitForOutputUs = 500000; // 0.5 seconds
-const int64_t kTimeoutWaitForInputUs = 5000; // 5 milliseconds
+const int64_t kTimeoutWaitForInputUs = 0; // don't wait
const int kTimeoutMaxRetries = 20;
//static
diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp
index cf4edae..6fd0805 100644
--- a/media/libstagefright/StagefrightMediaScanner.cpp
+++ b/media/libstagefright/StagefrightMediaScanner.cpp
@@ -35,7 +35,7 @@
StagefrightMediaScanner::~StagefrightMediaScanner() {}
-static std::unordered_set<std::string> gSupportedExtensions;
+static std::vector<std::string> gSupportedExtensions;
static bool FileHasAcceptableExtension(const char *extension) {
@@ -44,7 +44,12 @@
gSupportedExtensions = MediaExtractorFactory::getSupportedTypes();
}
- return gSupportedExtensions.count(std::string(extension + 1)) != 0;
+ for (auto ext: gSupportedExtensions) {
+ if (ext == (extension + 1)) {
+ return true;
+ }
+ }
+ return false;
}
MediaScanResult StagefrightMediaScanner::processFile(
@@ -158,7 +163,11 @@
if (mRetriever->setDataSource(fd, 0, size) == OK) {
sp<IMemory> mem = mRetriever->extractAlbumArt();
if (mem != NULL) {
- MediaAlbumArt *art = static_cast<MediaAlbumArt *>(mem->pointer());
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ MediaAlbumArt *art = static_cast<MediaAlbumArt *>(mem->unsecurePointer());
return art->clone();
}
}
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/Utils.cpp b/media/libstagefright/Utils.cpp
index 02e8ab5..a1e4d43 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -227,6 +227,68 @@
}
}
+static void parseDolbyVisionProfileLevelFromDvcc(const uint8_t *ptr, size_t size, sp<AMessage> &format) {
+ // dv_major.dv_minor Should be 1.0 or 2.1
+ if (size != 24 || ((ptr[0] != 1 || ptr[1] != 0) && (ptr[0] != 2 || ptr[1] != 1))) {
+ ALOGV("Size %zu, dv_major %d, dv_minor %d", size, ptr[0], ptr[1]);
+ return;
+ }
+
+ const uint8_t profile = ptr[2] >> 1;
+ const uint8_t level = ((ptr[2] & 0x1) << 5) | ((ptr[3] >> 3) & 0x1f);
+ const uint8_t rpu_present_flag = (ptr[3] >> 2) & 0x01;
+ const uint8_t el_present_flag = (ptr[3] >> 1) & 0x01;
+ const uint8_t bl_present_flag = (ptr[3] & 0x01);
+ const int32_t bl_compatibility_id = (int32_t)(ptr[4] >> 4);
+
+ ALOGV("profile-level-compatibility value in dv(c|v)c box %d-%d-%d",
+ profile, level, bl_compatibility_id);
+
+ // All Dolby Profiles will have profile and level info in MediaFormat
+ // Profile 8 and 9 will have bl_compatibility_id too.
+ const static ALookup<uint8_t, OMX_VIDEO_DOLBYVISIONPROFILETYPE> profiles{
+ {1, OMX_VIDEO_DolbyVisionProfileDvavPen},
+ {3, OMX_VIDEO_DolbyVisionProfileDvheDen},
+ {4, OMX_VIDEO_DolbyVisionProfileDvheDtr},
+ {5, OMX_VIDEO_DolbyVisionProfileDvheStn},
+ {6, OMX_VIDEO_DolbyVisionProfileDvheDth},
+ {7, OMX_VIDEO_DolbyVisionProfileDvheDtb},
+ {8, OMX_VIDEO_DolbyVisionProfileDvheSt},
+ {9, OMX_VIDEO_DolbyVisionProfileDvavSe},
+ {10, OMX_VIDEO_DolbyVisionProfileDvav110},
+ };
+
+ const static ALookup<uint8_t, OMX_VIDEO_DOLBYVISIONLEVELTYPE> levels{
+ {0, OMX_VIDEO_DolbyVisionLevelUnknown},
+ {1, OMX_VIDEO_DolbyVisionLevelHd24},
+ {2, OMX_VIDEO_DolbyVisionLevelHd30},
+ {3, OMX_VIDEO_DolbyVisionLevelFhd24},
+ {4, OMX_VIDEO_DolbyVisionLevelFhd30},
+ {5, OMX_VIDEO_DolbyVisionLevelFhd60},
+ {6, OMX_VIDEO_DolbyVisionLevelUhd24},
+ {7, OMX_VIDEO_DolbyVisionLevelUhd30},
+ {8, OMX_VIDEO_DolbyVisionLevelUhd48},
+ {9, OMX_VIDEO_DolbyVisionLevelUhd60},
+ };
+ // set rpuAssoc
+ if (rpu_present_flag && el_present_flag && !bl_present_flag) {
+ format->setInt32("rpuAssoc", 1);
+ }
+ // set profile & level if they are recognized
+ OMX_VIDEO_DOLBYVISIONPROFILETYPE codecProfile;
+ OMX_VIDEO_DOLBYVISIONLEVELTYPE codecLevel;
+ if (profiles.map(profile, &codecProfile)) {
+ format->setInt32("profile", codecProfile);
+ if (codecProfile == OMX_VIDEO_DolbyVisionProfileDvheSt ||
+ codecProfile == OMX_VIDEO_DolbyVisionProfileDvavSe) {
+ format->setInt32("bl_compatibility_id", bl_compatibility_id);
+ }
+ if (levels.map(level, &codecLevel)) {
+ format->setInt32("level", codecLevel);
+ }
+ }
+}
+
static void parseH263ProfileLevelFromD263(const uint8_t *ptr, size_t size, sp<AMessage> &format) {
if (size < 7) {
return;
@@ -1406,6 +1468,12 @@
msg->setBuffer("csd-0", buffer);
}
+ if (meta->findData(kKeyDVCC, &type, &data, &size)) {
+ const uint8_t *ptr = (const uint8_t *)data;
+ ALOGV("DV: calling parseDolbyVisionProfileLevelFromDvcc with data size %zu", size);
+ parseDolbyVisionProfileLevelFromDvcc(ptr, size, msg);
+ }
+
*format = msg;
return OK;
@@ -1801,7 +1869,7 @@
if (msg->findInt32("frame-rate", &fps) && fps > 0) {
meta->setInt32(kKeyFrameRate, fps);
} else if (msg->findFloat("frame-rate", &fpsFloat)
- && fpsFloat >= 1 && static_cast<int32_t>(fpsFloat) <= INT32_MAX) {
+ && fpsFloat >= 1 && fpsFloat <= (float)INT32_MAX) {
// truncate values to distinguish between e.g. 24 vs 23.976 fps
meta->setInt32(kKeyFrameRate, (int32_t)fpsFloat);
}
@@ -1834,6 +1902,31 @@
meta->setData(kKeyHVCC, kTypeHVCC, hvcc.data(), outsize);
} else if (mime == MEDIA_MIMETYPE_VIDEO_AV1) {
meta->setData(kKeyAV1C, 0, csd0->data(), csd0->size());
+ } else if (mime == MEDIA_MIMETYPE_VIDEO_DOLBY_VISION) {
+ if (msg->findBuffer("csd-2", &csd2)) {
+ //dvcc should be 24
+ if (csd2->size() == 24) {
+ meta->setData(kKeyDVCC, kTypeDVCC, csd2->data(), csd2->size());
+ uint8_t *dvcc = csd2->data();
+ const uint8_t profile = dvcc[2] >> 1;
+ if (profile > 1 && profile < 9) {
+ std::vector<uint8_t> hvcc(csd0size + 1024);
+ size_t outsize = reassembleHVCC(csd0, hvcc.data(), hvcc.size(), 4);
+ meta->setData(kKeyHVCC, kTypeHVCC, hvcc.data(), outsize);
+ } else if (DolbyVisionProfileDvav110 == profile) {
+ meta->setData(kKeyAV1C, 0, csd0->data(), csd0->size());
+ } else {
+ sp<ABuffer> csd1;
+ if (msg->findBuffer("csd-1", &csd1)) {
+ std::vector<char> avcc(csd0size + csd1->size() + 1024);
+ size_t outsize = reassembleAVCC(csd0, csd1, avcc.data());
+ meta->setData(kKeyAVCC, kTypeAVCC, avcc.data(), outsize);
+ }
+ }
+ }
+ } else {
+ ALOGW("We need csd-2!!. %s", msg->debugString().c_str());
+ }
} else if (mime == MEDIA_MIMETYPE_VIDEO_VP9) {
meta->setData(kKeyVp9CodecPrivate, 0, csd0->data(), csd0->size());
} else if (mime == MEDIA_MIMETYPE_AUDIO_OPUS) {
@@ -1880,8 +1973,18 @@
meta->setData(kKeyStreamHeader, 'mdat', csd0->data(), csd0->size());
} else if (msg->findBuffer("d263", &csd0)) {
meta->setData(kKeyD263, kTypeD263, csd0->data(), csd0->size());
- }
+ } else if (mime == MEDIA_MIMETYPE_VIDEO_DOLBY_VISION && msg->findBuffer("csd-2", &csd2)) {
+ meta->setData(kKeyDVCC, kTypeDVCC, csd2->data(), csd2->size());
+ // Remove CSD-2 from the data here to avoid duplicate data in meta
+ meta->remove(kKeyOpaqueCSD2);
+
+ if (msg->findBuffer("csd-avc", &csd0)) {
+ meta->setData(kKeyAVCC, kTypeAVCC, csd0->data(), csd0->size());
+ } else if (msg->findBuffer("csd-hevc", &csd0)) {
+ meta->setData(kKeyHVCC, kTypeHVCC, csd0->data(), csd0->size());
+ }
+ }
// XXX TODO add whatever other keys there are
#if 0
diff --git a/media/libstagefright/bqhelper/GraphicBufferSource.cpp b/media/libstagefright/bqhelper/GraphicBufferSource.cpp
index 59317e7..de9d12c 100644
--- a/media/libstagefright/bqhelper/GraphicBufferSource.cpp
+++ b/media/libstagefright/bqhelper/GraphicBufferSource.cpp
@@ -29,6 +29,7 @@
#include <media/stagefright/foundation/ColorUtils.h>
#include <media/stagefright/foundation/FileDescriptor.h>
+#include <android-base/properties.h>
#include <media/hardware/MetadataBufferType.h>
#include <ui/GraphicBuffer.h>
#include <gui/BufferItem.h>
@@ -800,6 +801,9 @@
}
}
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
bool GraphicBufferSource::calculateCodecTimestamp_l(
nsecs_t bufferTimeNs, int64_t *codecTimeUs) {
int64_t timeUs = bufferTimeNs / 1000;
@@ -816,14 +820,15 @@
mPrevFrameUs = mBaseFrameUs =
std::llround((timeUs * mCaptureFps) / mFps);
mFrameCount = 0;
- } else {
- // snap to nearest capture point
+ } else if (mSnapTimestamps) {
double nFrames = (timeUs - mPrevCaptureUs) * mCaptureFps / 1000000;
if (nFrames < 0.5 - kTimestampFluctuation) {
// skip this frame as it's too close to previous capture
- ALOGD("skipping frame, timeUs %lld", static_cast<long long>(timeUs));
+ ALOGD("skipping frame, timeUs %lld",
+ static_cast<long long>(timeUs));
return false;
}
+ // snap to nearest capture point
if (nFrames <= 1.0) {
nFrames = 1.0;
}
@@ -832,6 +837,22 @@
mFrameCount * 1000000 / mCaptureFps);
mPrevFrameUs = mBaseFrameUs + std::llround(
mFrameCount * 1000000 / mFps);
+ } else {
+ if (timeUs <= mPrevCaptureUs) {
+ if (mFrameDropper != NULL && mFrameDropper->disabled()) {
+ // Warn only, client has disabled frame drop logic possibly for image
+ // encoding cases where camera's ZSL mode could send out of order frames.
+ ALOGW("Received frame that's going backward in time");
+ } else {
+ // Drop the frame if it's going backward in time. Bad timestamp
+ // could disrupt encoder's rate control completely.
+ ALOGW("Dropping frame that's going backward in time");
+ return false;
+ }
+ }
+ mPrevCaptureUs = timeUs;
+ mPrevFrameUs = mBaseFrameUs + std::llround(
+ (timeUs - mBaseCaptureUs) * (mCaptureFps / mFps));
}
ALOGV("timeUs %lld, captureUs %lld, frameUs %lld",
@@ -1359,6 +1380,12 @@
mFps = fps;
mCaptureFps = captureFps;
+ if (captureFps > fps) {
+ mSnapTimestamps = 1 == base::GetIntProperty(
+ "debug.stagefright.snap_timestamps", int64_t(0));
+ } else {
+ mSnapTimestamps = false;
+ }
return OK;
}
diff --git a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h
index b4c4d4a..ed5d7cb 100644
--- a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h
+++ b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h
@@ -461,12 +461,33 @@
// Slow motion mode is enabled if both encoding and capture frame rates are
// defined and the encoding frame rate is less than half the capture frame
// rate. In this mode, the source is expected to produce frames with an even
- // timestamp interval (after rounding) with the configured capture fps. The
- // first source timestamp is used as the source base time. Afterwards, the
- // timestamp of each source frame is snapped to the nearest expected capture
- // timestamp and scaled to match the configured encoding frame rate.
+ // timestamp interval (after rounding) with the configured capture fps.
+ //
+ // These modes must be configured by calling setTimeLapseConfig() before
+ // using this source.
+ //
+ // Timestamp snapping for slow motion recording
+ // ============================================
+ //
+ // When the slow motion mode is configured with setTimeLapseConfig(), the
+ // property "debug.stagefright.snap_timestamps" will be checked. If the
+ // value of the property is set to any value other than 1, mSnapTimestamps
+ // will be set to false. Otherwise, mSnapTimestamps will be set to true.
+ // (mSnapTimestamps will be false for time lapse recording regardless of the
+ // value of the property.)
+ //
+ // If mSnapTimestamps is true, i.e., timestamp snapping is enabled, the
+ // first source timestamp will be used as the source base time; afterwards,
+ // the timestamp of each source frame will be snapped to the nearest
+ // expected capture timestamp and scaled to match the configured encoding
+ // frame rate.
+ //
+ // If timestamp snapping is disabled, the timestamp of source frames will
+ // be scaled to match the ratio between the configured encoding frame rate
+ // and the configured capture frame rate.
- // These modes must be enabled before using this source.
+ // whether timestamps will be snapped
+ bool mSnapTimestamps{true};
// adjusted capture timestamp of the base frame
int64_t mBaseCaptureUs;
diff --git a/media/libstagefright/codecs/flac/enc/Android.bp b/media/libstagefright/codecs/flac/enc/Android.bp
index d7d871a..f35bce1 100644
--- a/media/libstagefright/codecs/flac/enc/Android.bp
+++ b/media/libstagefright/codecs/flac/enc/Android.bp
@@ -15,8 +15,10 @@
},
header_libs: ["libbase_headers"],
- static_libs: [
+ shared_libs: [
"libaudioutils",
+ ],
+ static_libs: [
"libFLAC",
],
}
diff --git a/media/libstagefright/codecs/mp3dec/src/pvmp3_alias_reduction.cpp b/media/libstagefright/codecs/mp3dec/src/pvmp3_alias_reduction.cpp
index a4f798e..6c8102b 100644
--- a/media/libstagefright/codecs/mp3dec/src/pvmp3_alias_reduction.cpp
+++ b/media/libstagefright/codecs/mp3dec/src/pvmp3_alias_reduction.cpp
@@ -169,7 +169,7 @@
int32 i, j;
- *used_freq_lines = fxp_mul32_Q32(*used_freq_lines << 16, (int32)((float)0x7FFFFFFF / 18.0f - 1.0f)) >> 15;
+ *used_freq_lines = fxp_mul32_Q32(*used_freq_lines << 16, (int32)((float)0x7FFFFFFF / (float)18 - 1.0f)) >> 15;
if (gr_info->window_switching_flag && gr_info->block_type == 2)
diff --git a/media/libstagefright/codecs/on2/enc/Android.bp b/media/libstagefright/codecs/on2/enc/Android.bp
index cd69e0d..705e554 100644
--- a/media/libstagefright/codecs/on2/enc/Android.bp
+++ b/media/libstagefright/codecs/on2/enc/Android.bp
@@ -21,4 +21,5 @@
},
shared_libs: ["libvpx"],
+ header_libs: ["libbase_headers"],
}
diff --git a/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp b/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp
index da86758..87e8fd4 100644
--- a/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp
+++ b/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp
@@ -1426,75 +1426,90 @@
RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_DO_EXECUTE");
UWORD32 ui_exec_done;
+ WORD32 i_num_preroll = 0;
/* Checking for end of processing */
err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_EXECUTE, IA_CMD_TYPE_DONE_QUERY,
&ui_exec_done);
RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_DONE_QUERY");
-#ifdef ENABLE_MPEG_D_DRC
+ err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
+ IA_ENHAACPLUS_DEC_CONFIG_GET_NUM_PRE_ROLL_FRAMES,
+ &i_num_preroll);
+
+ RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GET_NUM_PRE_ROLL_FRAMES");
{
- if (ui_exec_done != 1) {
- VOID* p_array; // ITTIAM:buffer to handle gain payload
- WORD32 buf_size = 0; // ITTIAM:gain payload length
- WORD32 bit_str_fmt = 1;
- WORD32 gain_stream_flag = 1;
+ int32_t pi_preroll_frame_offset = 0;
+ do {
+#ifdef ENABLE_MPEG_D_DRC
+ if (ui_exec_done != 1) {
+ VOID* p_array; // ITTIAM:buffer to handle gain payload
+ WORD32 buf_size = 0; // ITTIAM:gain payload length
+ WORD32 bit_str_fmt = 1;
+ WORD32 gain_stream_flag = 1;
- err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
- IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_LEN, &buf_size);
- RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_LEN");
+ err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
+ IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_LEN, &buf_size);
+ RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_LEN");
- err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
- IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_BUF, &p_array);
- RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_BUF");
+ err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
+ IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_BUF, &p_array);
+ RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_BUF");
- if (buf_size > 0) {
- /*Set bitstream_split_format */
- err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
- IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT, &bit_str_fmt);
- RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+ if (buf_size > 0) {
+ /*Set bitstream_split_format */
+ err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
+ IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT, &bit_str_fmt);
+ RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
- memcpy(mDrcInBuf, p_array, buf_size);
- /* Set number of bytes to be processed */
- err_code =
- ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES_BS, 0, &buf_size);
- RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+ memcpy(mDrcInBuf, p_array, buf_size);
+ /* Set number of bytes to be processed */
+ err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES_BS,
+ 0, &buf_size);
+ RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
- err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
- IA_DRC_DEC_CONFIG_GAIN_STREAM_FLAG, &gain_stream_flag);
- RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+ err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
+ IA_DRC_DEC_CONFIG_GAIN_STREAM_FLAG,
+ &gain_stream_flag);
+ RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
- /* Execute process */
- err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT,
- IA_CMD_TYPE_INIT_CPY_BSF_BUFF, NULL);
- RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+ /* Execute process */
+ err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT,
+ IA_CMD_TYPE_INIT_CPY_BSF_BUFF, NULL);
+ RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
- mMpegDDRCPresent = 1;
+ mMpegDDRCPresent = 1;
+ }
}
- }
- }
#endif
- /* How much buffer is used in input buffers */
- err_code =
- ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CURIDX_INPUT_BUF, 0, bytesConsumed);
- RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_CURIDX_INPUT_BUF");
+ /* How much buffer is used in input buffers */
+ err_code =
+ ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CURIDX_INPUT_BUF,
+ 0, bytesConsumed);
+ RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_CURIDX_INPUT_BUF");
- /* Get the output bytes */
- err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_OUTPUT_BYTES, 0, outBytes);
- RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_OUTPUT_BYTES");
+ /* Get the output bytes */
+ err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+ IA_API_CMD_GET_OUTPUT_BYTES, 0, outBytes);
+ RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_OUTPUT_BYTES");
#ifdef ENABLE_MPEG_D_DRC
- if (mMpegDDRCPresent == 1) {
- memcpy(mDrcInBuf, mOutputBuffer, *outBytes);
- err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES, 0, outBytes);
- RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_INPUT_BYTES");
+ if (mMpegDDRCPresent == 1) {
+ memcpy(mDrcInBuf, mOutputBuffer + pi_preroll_frame_offset, *outBytes);
+ pi_preroll_frame_offset += *outBytes;
+ err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES,
+ 0, outBytes);
+ RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_INPUT_BYTES");
- err_code =
- ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_EXECUTE, IA_CMD_TYPE_DO_EXECUTE, NULL);
- RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_DO_EXECUTE");
+ err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_EXECUTE,
+ IA_CMD_TYPE_DO_EXECUTE, NULL);
+ RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_DO_EXECUTE");
- memcpy(mOutputBuffer, mDrcOutBuf, *outBytes);
- }
+ memcpy(mOutputBuffer, mDrcOutBuf, *outBytes);
+ }
#endif
+ i_num_preroll--;
+ } while (i_num_preroll > 0);
+ }
return IA_NO_ERROR;
}
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/colorconversion/ColorConverter.cpp b/media/libstagefright/colorconversion/ColorConverter.cpp
index d685321..c7dc415 100644
--- a/media/libstagefright/colorconversion/ColorConverter.cpp
+++ b/media/libstagefright/colorconversion/ColorConverter.cpp
@@ -324,8 +324,8 @@
}
#define DECLARE_YUV2RGBFUNC(func, rgb) int (*func)( \
- const uint8*, int, const uint8*, int, \
- const uint8*, int, uint8*, int, int, int) \
+ const uint8_t*, int, const uint8_t*, int, \
+ const uint8_t*, int, uint8_t*, int, int, int) \
= mSrcColorSpace.isBt709() ? libyuv::H420To##rgb \
: mSrcColorSpace.isJpeg() ? libyuv::J420To##rgb \
: libyuv::I420To##rgb
@@ -350,7 +350,7 @@
{
DECLARE_YUV2RGBFUNC(func, RGB565);
(*func)(src_y, src.mStride, src_u, src.mStride / 2, src_v, src.mStride / 2,
- (uint8 *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight());
+ (uint8_t *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight());
break;
}
@@ -358,7 +358,7 @@
{
DECLARE_YUV2RGBFUNC(func, ABGR);
(*func)(src_y, src.mStride, src_u, src.mStride / 2, src_v, src.mStride / 2,
- (uint8 *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight());
+ (uint8_t *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight());
break;
}
@@ -366,7 +366,7 @@
{
DECLARE_YUV2RGBFUNC(func, ARGB);
(*func)(src_y, src.mStride, src_u, src.mStride / 2, src_v, src.mStride / 2,
- (uint8 *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight());
+ (uint8_t *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight());
break;
}
@@ -391,17 +391,17 @@
switch (mDstFormat) {
case OMX_COLOR_Format16bitRGB565:
- libyuv::NV12ToRGB565(src_y, src.mStride, src_u, src.mStride, (uint8 *)dst_ptr,
+ libyuv::NV12ToRGB565(src_y, src.mStride, src_u, src.mStride, (uint8_t *)dst_ptr,
dst.mStride, src.cropWidth(), src.cropHeight());
break;
case OMX_COLOR_Format32bitBGRA8888:
- libyuv::NV12ToARGB(src_y, src.mStride, src_u, src.mStride, (uint8 *)dst_ptr,
+ libyuv::NV12ToARGB(src_y, src.mStride, src_u, src.mStride, (uint8_t *)dst_ptr,
dst.mStride, src.cropWidth(), src.cropHeight());
break;
case OMX_COLOR_Format32BitRGBA8888:
- libyuv::NV12ToABGR(src_y, src.mStride, src_u, src.mStride, (uint8 *)dst_ptr,
+ libyuv::NV12ToABGR(src_y, src.mStride, src_u, src.mStride, (uint8_t *)dst_ptr,
dst.mStride, src.cropWidth(), src.cropHeight());
break;
diff --git a/media/libstagefright/exports.lds b/media/libstagefright/exports.lds
index aabc233..f5ddf1e 100644
--- a/media/libstagefright/exports.lds
+++ b/media/libstagefright/exports.lds
@@ -395,7 +395,6 @@
ScaleFilterCols_NEON*;
ScaleFilterReduce;
ScaleFilterRows_NEON*;
- ScaleOffset;
ScalePlane;
ScalePlane_16;
ScalePlaneBilinearDown;
@@ -505,4 +504,8 @@
YUY2ToYRow_Any_NEON*;
YUY2ToYRow_C;
YUY2ToYRow_NEON*;
+ ogg_packet_*;
+ ogg_page_*;
+ ogg_stream_*;
+ ogg_sync_*;
};
diff --git a/media/libstagefright/filters/MediaFilter.cpp b/media/libstagefright/filters/MediaFilter.cpp
index 777ab5b..c7baa73 100644
--- a/media/libstagefright/filters/MediaFilter.cpp
+++ b/media/libstagefright/filters/MediaFilter.cpp
@@ -20,13 +20,12 @@
#include <inttypes.h>
#include <utils/Trace.h>
-#include <binder/MemoryDealer.h>
-
-#include <media/stagefright/BufferProducerWrapper.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/BufferProducerWrapper.h>
+#include <media/stagefright/MediaCodecConstants.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaFilter.h>
@@ -42,11 +41,121 @@
#include "SaturationFilter.h"
#include "ZeroFilter.h"
-#include "../include/ACodecBufferChannel.h"
-#include "../include/SharedMemoryBuffer.h"
-
namespace android {
+class MediaFilter::BufferChannel : public BufferChannelBase {
+public:
+ BufferChannel(const sp<AMessage> &in, const sp<AMessage> &out)
+ : mInputBufferFilled(in), mOutputBufferDrained(out) {
+ }
+
+ ~BufferChannel() override = default;
+
+ // BufferChannelBase
+
+ status_t queueInputBuffer(const sp<MediaCodecBuffer> &buffer) override {
+ sp<AMessage> msg = mInputBufferFilled->dup();
+ msg->setObject("buffer", buffer);
+ msg->post();
+ return OK;
+ }
+
+ status_t queueSecureInputBuffer(
+ const sp<MediaCodecBuffer> &,
+ bool,
+ const uint8_t *,
+ const uint8_t *,
+ CryptoPlugin::Mode,
+ CryptoPlugin::Pattern,
+ const CryptoPlugin::SubSample *,
+ size_t,
+ AString *) override {
+ return INVALID_OPERATION;
+ }
+
+ status_t renderOutputBuffer(
+ const sp<MediaCodecBuffer> &buffer, int64_t /* timestampNs */) override {
+ sp<AMessage> msg = mOutputBufferDrained->dup();
+ msg->setObject("buffer", buffer);
+ msg->post();
+ return OK;
+ }
+
+ status_t discardBuffer(const sp<MediaCodecBuffer> &buffer) override {
+ if (FindBufferIndex(&mInputBuffers, buffer) >= 0) {
+ sp<AMessage> msg = mInputBufferFilled->dup();
+ msg->setObject("buffer", buffer);
+ msg->post();
+ return OK;
+ }
+ sp<AMessage> msg = mOutputBufferDrained->dup();
+ msg->setObject("buffer", buffer);
+ msg->post();
+ return OK;
+ }
+
+ void getInputBufferArray(Vector<sp<MediaCodecBuffer>> *array) {
+ if (!array) {
+ return;
+ }
+ array->clear();
+ array->appendVector(mInputBuffers);
+ }
+
+ void getOutputBufferArray(Vector<sp<MediaCodecBuffer>> *array) {
+ if (!array) {
+ return;
+ }
+ array->clear();
+ array->appendVector(mOutputBuffers);
+ }
+
+ // For MediaFilter
+
+ void fillThisBuffer(const sp<MediaCodecBuffer> &buffer) {
+ ssize_t index = FindBufferIndex(&mInputBuffers, buffer);
+ mCallback->onInputBufferAvailable(index, buffer);
+ }
+
+ void drainThisBuffer(const sp<MediaCodecBuffer> &buffer, int flags) {
+ ssize_t index = FindBufferIndex(&mOutputBuffers, buffer);
+ buffer->meta()->setInt32("flags", flags);
+ mCallback->onOutputBufferAvailable(index, buffer);
+ }
+
+ template <class T>
+ void setInputBuffers(T begin, T end) {
+ mInputBuffers.clear();
+ for (T it = begin; it != end; ++it) {
+ mInputBuffers.push_back(it->mData);
+ }
+ }
+
+ template <class T>
+ void setOutputBuffers(T begin, T end) {
+ mOutputBuffers.clear();
+ for (T it = begin; it != end; ++it) {
+ mOutputBuffers.push_back(it->mData);
+ }
+ }
+
+private:
+ sp<AMessage> mInputBufferFilled;
+ sp<AMessage> mOutputBufferDrained;
+ Vector<sp<MediaCodecBuffer>> mInputBuffers;
+ Vector<sp<MediaCodecBuffer>> mOutputBuffers;
+
+ static ssize_t FindBufferIndex(
+ Vector<sp<MediaCodecBuffer>> *array, const sp<MediaCodecBuffer> &buffer) {
+ for (size_t i = 0; i < array->size(); ++i) {
+ if (array->itemAt(i) == buffer) {
+ return i;
+ }
+ }
+ return -1;
+ }
+};
+
// parameter: number of input and output buffers
static const size_t kBufferCountActual = 4;
@@ -54,9 +163,6 @@
: mState(UNINITIALIZED),
mGeneration(0),
mGraphicBufferListener(NULL) {
- mBufferChannel = std::make_shared<ACodecBufferChannel>(
- new AMessage(kWhatInputBufferFilled, this),
- new AMessage(kWhatOutputBufferDrained, this));
}
MediaFilter::~MediaFilter() {
@@ -65,6 +171,11 @@
//////////////////// PUBLIC FUNCTIONS //////////////////////////////////////////
std::shared_ptr<BufferChannelBase> MediaFilter::getBufferChannel() {
+ if (!mBufferChannel) {
+ mBufferChannel = std::make_shared<BufferChannel>(
+ new AMessage(kWhatInputBufferFilled, this),
+ new AMessage(kWhatOutputBufferDrained, this));
+ }
return mBufferChannel;
}
@@ -212,28 +323,23 @@
const bool isInput = portIndex == kPortIndexInput;
const size_t bufferSize = isInput ? mMaxInputSize : mMaxOutputSize;
- CHECK(mDealer[portIndex] == NULL);
CHECK(mBuffers[portIndex].isEmpty());
ALOGV("Allocating %zu buffers of size %zu on %s port",
kBufferCountActual, bufferSize,
isInput ? "input" : "output");
- size_t totalSize = kBufferCountActual * bufferSize;
-
- mDealer[portIndex] = new MemoryDealer(totalSize, "MediaFilter");
-
+ // trigger output format change
+ sp<AMessage> outputFormat = mOutputFormat->dup();
for (size_t i = 0; i < kBufferCountActual; ++i) {
- sp<IMemory> mem = mDealer[portIndex]->allocate(bufferSize);
- CHECK(mem.get() != NULL);
-
BufferInfo info;
info.mStatus = BufferInfo::OWNED_BY_US;
info.mBufferID = i;
info.mGeneration = mGeneration;
info.mOutputFlags = 0;
- info.mData = new SharedMemoryBuffer(
- isInput ? mInputFormat : mOutputFormat, mem);
+ info.mData = new MediaCodecBuffer(
+ isInput ? mInputFormat : outputFormat,
+ new ABuffer(bufferSize));
info.mData->meta()->setInt64("timeUs", 0);
mBuffers[portIndex].push_back(info);
@@ -243,27 +349,24 @@
&mBuffers[portIndex].editItemAt(i));
}
}
-
- std::vector<ACodecBufferChannel::BufferAndId> array(mBuffers[portIndex].size());
- for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) {
- array[i] = {mBuffers[portIndex][i].mData, mBuffers[portIndex][i].mBufferID};
- }
- if (portIndex == kPortIndexInput) {
- mBufferChannel->setInputBufferArray(array);
+ if (isInput) {
+ mBufferChannel->setInputBuffers(
+ mBuffers[portIndex].begin(), mBuffers[portIndex].end());
} else {
- mBufferChannel->setOutputBufferArray(array);
+ mBufferChannel->setOutputBuffers(
+ mBuffers[portIndex].begin(), mBuffers[portIndex].end());
}
return OK;
}
-MediaFilter::BufferInfo* MediaFilter::findBufferByID(
- uint32_t portIndex, IOMX::buffer_id bufferID,
+MediaFilter::BufferInfo* MediaFilter::findBuffer(
+ uint32_t portIndex, const sp<MediaCodecBuffer> &buffer,
ssize_t *index) {
for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) {
BufferInfo *info = &mBuffers[portIndex].editItemAt(i);
- if (info->mBufferID == bufferID) {
+ if (info->mData == buffer) {
if (index != NULL) {
*index = i;
}
@@ -293,7 +396,7 @@
info->mStatus = BufferInfo::OWNED_BY_UPSTREAM;
- mBufferChannel->fillThisBuffer(info->mBufferID);
+ mBufferChannel->fillThisBuffer(info->mData);
}
void MediaFilter::postDrainThisBuffer(BufferInfo *info) {
@@ -304,7 +407,7 @@
sp<AMessage> reply = new AMessage(kWhatOutputBufferDrained, this);
reply->setInt32("buffer-id", info->mBufferID);
- mBufferChannel->drainThisBuffer(info->mBufferID, info->mOutputFlags);
+ mBufferChannel->drainThisBuffer(info->mData, info->mOutputFlags);
info->mStatus = BufferInfo::OWNED_BY_UPSTREAM;
}
@@ -359,7 +462,7 @@
outputInfo->mOutputFlags = 0;
int32_t eos = 0;
if (inputInfo->mData->meta()->findInt32("eos", &eos) && eos != 0) {
- outputInfo->mOutputFlags |= OMX_BUFFERFLAG_EOS;
+ outputInfo->mOutputFlags |= BUFFER_FLAG_END_OF_STREAM;
mPortEOS[kPortIndexOutput] = true;
outputInfo->mData->meta()->setInt32("eos", eos);
postEOS();
@@ -400,8 +503,7 @@
return;
}
- // HACK - need "OMX.google" to use MediaCodec's software renderer
- mCallback->onComponentAllocated("OMX.google.MediaFilter");
+ mCallback->onComponentAllocated(mComponentName.c_str());
mState = INITIALIZED;
ALOGV("Handled kWhatAllocateComponent.");
}
@@ -477,6 +579,7 @@
mOutputFormat->setRect("crop", 0, 0, mStride, mSliceHeight);
mOutputFormat->setInt32("width", mWidth);
mOutputFormat->setInt32("height", mHeight);
+ mOutputFormat->setInt32("using-sw-renderer", 1);
mCallback->onComponentConfigured(mInputFormat, mOutputFormat);
mState = CONFIGURED;
@@ -509,9 +612,11 @@
}
void MediaFilter::onInputBufferFilled(const sp<AMessage> &msg) {
- IOMX::buffer_id bufferID;
- CHECK(msg->findInt32("buffer-id", (int32_t*)&bufferID));
- BufferInfo *info = findBufferByID(kPortIndexInput, bufferID);
+ sp<RefBase> obj;
+ CHECK(msg->findObject("buffer", &obj));
+ sp<MediaCodecBuffer> buffer = static_cast<MediaCodecBuffer *>(obj.get());
+ ssize_t index = -1;
+ BufferInfo *info = findBuffer(kPortIndexInput, buffer, &index);
if (mState != STARTED) {
// we're not running, so we'll just keep that buffer...
@@ -520,7 +625,7 @@
}
if (info->mGeneration != mGeneration) {
- ALOGV("Caught a stale input buffer [ID %d]", bufferID);
+ ALOGV("Caught a stale input buffer [index %zd]", index);
// buffer is stale (taken before a flush/shutdown) - repost it
CHECK_EQ(info->mStatus, BufferInfo::OWNED_BY_US);
postFillThisBuffer(info);
@@ -530,30 +635,9 @@
CHECK_EQ(info->mStatus, BufferInfo::OWNED_BY_UPSTREAM);
info->mStatus = BufferInfo::OWNED_BY_US;
- sp<MediaCodecBuffer> buffer;
int32_t err = OK;
bool eos = false;
- sp<RefBase> obj;
- if (!msg->findObject("buffer", &obj)) {
- // these are unfilled buffers returned by client
- CHECK(msg->findInt32("err", &err));
-
- if (err == OK) {
- // buffers with no errors are returned on MediaCodec.flush
- ALOGV("saw unfilled buffer (MediaCodec.flush)");
- postFillThisBuffer(info);
- return;
- } else {
- ALOGV("saw error %d instead of an input buffer", err);
- eos = true;
- }
-
- buffer.clear();
- } else {
- buffer = static_cast<MediaCodecBuffer *>(obj.get());
- }
-
int32_t isCSD;
if (buffer != NULL && buffer->meta()->findInt32("csd", &isCSD)
&& isCSD != 0) {
@@ -577,13 +661,15 @@
mInputEOSResult = err;
}
- ALOGV("Handled kWhatInputBufferFilled. [ID %u]", bufferID);
+ ALOGV("Handled kWhatInputBufferFilled. [index %zd]", index);
}
void MediaFilter::onOutputBufferDrained(const sp<AMessage> &msg) {
- IOMX::buffer_id bufferID;
- CHECK(msg->findInt32("buffer-id", (int32_t*)&bufferID));
- BufferInfo *info = findBufferByID(kPortIndexOutput, bufferID);
+ sp<RefBase> obj;
+ CHECK(msg->findObject("buffer", &obj));
+ sp<MediaCodecBuffer> buffer = static_cast<MediaCodecBuffer *>(obj.get());
+ ssize_t index = -1;
+ BufferInfo *info = findBuffer(kPortIndexOutput, buffer, &index);
if (mState != STARTED) {
// we're not running, so we'll just keep that buffer...
@@ -592,7 +678,7 @@
}
if (info->mGeneration != mGeneration) {
- ALOGV("Caught a stale output buffer [ID %d]", bufferID);
+ ALOGV("Caught a stale output buffer [index %zd]", index);
// buffer is stale (taken before a flush/shutdown) - keep it
CHECK_EQ(info->mStatus, BufferInfo::OWNED_BY_US);
return;
@@ -605,8 +691,7 @@
processBuffers();
- ALOGV("Handled kWhatOutputBufferDrained. [ID %u]",
- bufferID);
+ ALOGV("Handled kWhatOutputBufferDrained. [index %zd]", index);
}
void MediaFilter::onShutdown(const sp<AMessage> &msg) {
@@ -739,7 +824,7 @@
return;
}
- eosBuf->mOutputFlags = OMX_BUFFERFLAG_EOS;
+ eosBuf->mOutputFlags = BUFFER_FLAG_END_OF_STREAM;
eosBuf->mGeneration = mGeneration;
eosBuf->mData->setRange(0, 0);
postDrainThisBuffer(eosBuf);
diff --git a/media/libstagefright/flac/dec/Android.bp b/media/libstagefright/flac/dec/Android.bp
index b494e16..7ebe71f 100644
--- a/media/libstagefright/flac/dec/Android.bp
+++ b/media/libstagefright/flac/dec/Android.bp
@@ -1,4 +1,4 @@
-cc_library {
+cc_library_shared {
name: "libstagefright_flacdec",
vendor_available: true,
@@ -18,29 +18,20 @@
cfi: true,
},
- static: {
- whole_static_libs: [
- "libFLAC",
- "libaudioutils",
- ],
- },
-
- shared: {
- static_libs: [
- "libFLAC",
- "libaudioutils",
- ],
- export_static_lib_headers: [
- "libFLAC",
- ],
- },
-
shared_libs: [
+ "libaudioutils",
"liblog",
],
+ static_libs: [
+ "libFLAC",
+ ],
+
+ export_static_lib_headers: [
+ "libFLAC",
+ ],
+
header_libs: [
"libmedia_headers",
- "libFLAC-headers",
],
}
diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp
index df66ac6..7752bda 100644
--- a/media/libstagefright/foundation/AMessage.cpp
+++ b/media/libstagefright/foundation/AMessage.cpp
@@ -22,7 +22,6 @@
#include "AMessage.h"
-#include <binder/Parcel.h>
#include <log/log.h>
#include "AAtomizer.h"
@@ -34,6 +33,10 @@
#include <media/stagefright/foundation/hexdump.h>
+#ifndef __ANDROID_VNDK__
+#include <binder/Parcel.h>
+#endif
+
namespace android {
extern ALooperRoster gLooperRoster;
@@ -643,6 +646,7 @@
return s;
}
+#ifndef __ANDROID_VNDK__
// static
sp<AMessage> AMessage::FromParcel(const Parcel &parcel, size_t maxNestingLevel) {
int32_t what = parcel.readInt32();
@@ -809,6 +813,7 @@
}
}
}
+#endif // __ANDROID_VNDK__
sp<AMessage> AMessage::changesFrom(const sp<const AMessage> &other, bool deep) const {
if (other == NULL) {
diff --git a/media/libstagefright/foundation/AString.cpp b/media/libstagefright/foundation/AString.cpp
index a8adff5..4bd186c 100644
--- a/media/libstagefright/foundation/AString.cpp
+++ b/media/libstagefright/foundation/AString.cpp
@@ -23,11 +23,14 @@
#include <stdlib.h>
#include <string.h>
-#include <binder/Parcel.h>
#include <utils/String8.h>
#include "ADebug.h"
#include "AString.h"
+#ifndef __ANDROID_VNDK__
+#include <binder/Parcel.h>
+#endif
+
namespace android {
// static
@@ -362,6 +365,7 @@
return !strcasecmp(mData + mSize - suffixLen, suffix);
}
+#ifndef __ANDROID_VNDK__
// static
AString AString::FromParcel(const Parcel &parcel) {
size_t size = static_cast<size_t>(parcel.readInt32());
@@ -376,6 +380,7 @@
}
return err;
}
+#endif
AString AStringPrintf(const char *format, ...) {
va_list ap;
diff --git a/media/libstagefright/foundation/Android.bp b/media/libstagefright/foundation/Android.bp
index b95f054..7398690 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 {
@@ -79,6 +80,17 @@
"hexdump.cpp",
],
+ target: {
+ vendor: {
+ exclude_shared_libs: [
+ "libbinder",
+ ],
+ cflags: [
+ "-DNO_IMEMORY",
+ ],
+ },
+ },
+
clang: true,
sanitize: {
diff --git a/media/libstagefright/foundation/MediaBuffer.cpp b/media/libstagefright/foundation/MediaBuffer.cpp
index 9beac05..8e245dc 100644
--- a/media/libstagefright/foundation/MediaBuffer.cpp
+++ b/media/libstagefright/foundation/MediaBuffer.cpp
@@ -72,7 +72,7 @@
}
} else {
getSharedControl()->clear();
- mData = (uint8_t *)mMemory->pointer() + sizeof(SharedControl);
+ mData = (uint8_t *)mMemory->unsecurePointer() + sizeof(SharedControl);
ALOGV("Allocated shared mem buffer of size %zu @ %p", size, mData);
}
}
diff --git a/media/libstagefright/foundation/MediaBufferGroup.cpp b/media/libstagefright/foundation/MediaBufferGroup.cpp
index 84ff9a6..3c25047 100644
--- a/media/libstagefright/foundation/MediaBufferGroup.cpp
+++ b/media/libstagefright/foundation/MediaBufferGroup.cpp
@@ -75,7 +75,7 @@
for (size_t i = 0; i < buffers; ++i) {
sp<IMemory> mem = memoryDealer->allocate(augmented_size);
- if (mem.get() == nullptr || mem->pointer() == nullptr) {
+ if (mem.get() == nullptr || mem->unsecurePointer() == nullptr) {
ALOGW("Only allocated %zu shared buffers of size %zu", i, buffer_size);
break;
}
diff --git a/media/libstagefright/foundation/MetaData.cpp b/media/libstagefright/foundation/MetaData.cpp
index 1d0a607..8174597 100644
--- a/media/libstagefright/foundation/MetaData.cpp
+++ b/media/libstagefright/foundation/MetaData.cpp
@@ -17,7 +17,6 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "MetaData"
#include <inttypes.h>
-#include <binder/Parcel.h>
#include <utils/KeyedVector.h>
#include <utils/Log.h>
@@ -29,6 +28,10 @@
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/MetaData.h>
+#ifndef __ANDROID_VNDK__
+#include <binder/Parcel.h>
+#endif
+
namespace android {
@@ -45,6 +48,7 @@
MetaData::~MetaData() {
}
+#ifndef __ANDROID_VNDK__
/* static */
sp<MetaData> MetaData::createFromParcel(const Parcel &parcel) {
@@ -52,6 +56,7 @@
meta->updateFromParcel(parcel);
return meta;
}
+#endif
} // namespace android
diff --git a/media/libstagefright/foundation/MetaDataBase.cpp b/media/libstagefright/foundation/MetaDataBase.cpp
index bfea6f1..4b439c6 100644
--- a/media/libstagefright/foundation/MetaDataBase.cpp
+++ b/media/libstagefright/foundation/MetaDataBase.cpp
@@ -17,7 +17,6 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "MetaDataBase"
#include <inttypes.h>
-#include <binder/Parcel.h>
#include <utils/KeyedVector.h>
#include <utils/Log.h>
@@ -29,6 +28,10 @@
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/MetaDataBase.h>
+#ifndef __ANDROID_VNDK__
+#include <binder/Parcel.h>
+#endif
+
namespace android {
struct MetaDataBase::typed_data {
@@ -449,6 +452,7 @@
}
}
+#ifndef __ANDROID_VNDK__
status_t MetaDataBase::writeToParcel(Parcel &parcel) {
status_t ret;
size_t numItems = mInternalData->mItems.size();
@@ -528,6 +532,7 @@
ALOGW("no metadata in parcel");
return UNKNOWN_ERROR;
}
+#endif
} // namespace android
diff --git a/media/libstagefright/foundation/OpusHeader.cpp b/media/libstagefright/foundation/OpusHeader.cpp
index 513e41f..f5687e0 100644
--- a/media/libstagefright/foundation/OpusHeader.cpp
+++ b/media/libstagefright/foundation/OpusHeader.cpp
@@ -292,6 +292,10 @@
*opusHeadSize = data_size;
return true;
} else if (memcmp(AOPUS_CSD_MARKER_PREFIX, data, AOPUS_CSD_MARKER_PREFIX_SIZE) == 0) {
+ if (data_size < AOPUS_UNIFIED_CSD_MINSIZE || data_size > AOPUS_UNIFIED_CSD_MAXSIZE) {
+ ALOGD("Unexpected size for unified opus csd %zu", data_size);
+ return false;
+ }
size_t i = 0;
bool found = false;
while (i <= data_size - AOPUS_MARKER_SIZE - AOPUS_LENGTH_SIZE) {
diff --git a/media/libstagefright/foundation/TEST_MAPPING b/media/libstagefright/foundation/TEST_MAPPING
new file mode 100644
index 0000000..3301c4b
--- /dev/null
+++ b/media/libstagefright/foundation/TEST_MAPPING
@@ -0,0 +1,5 @@
+{
+ "presubmit": [
+ { "name": "sf_foundation_test" }
+ ]
+}
diff --git a/media/libstagefright/foundation/avc_utils.cpp b/media/libstagefright/foundation/avc_utils.cpp
index e8a6083..f53d2c9 100644
--- a/media/libstagefright/foundation/avc_utils.cpp
+++ b/media/libstagefright/foundation/avc_utils.cpp
@@ -166,10 +166,21 @@
unsigned pic_height_in_map_units_minus1 = parseUE(&br);
unsigned frame_mbs_only_flag = br.getBits(1);
- *width = pic_width_in_mbs_minus1 * 16 + 16;
+ // *width = pic_width_in_mbs_minus1 * 16 + 16;
+ if (__builtin_mul_overflow(pic_width_in_mbs_minus1, 16, &pic_width_in_mbs_minus1) ||
+ __builtin_add_overflow(pic_width_in_mbs_minus1, 16, width)) {
+ *width = 0;
+ }
- *height = (2 - frame_mbs_only_flag)
- * (pic_height_in_map_units_minus1 * 16 + 16);
+ // *height = (2 - frame_mbs_only_flag) * (pic_height_in_map_units_minus1 * 16 + 16);
+ if (__builtin_mul_overflow(
+ pic_height_in_map_units_minus1, 16, &pic_height_in_map_units_minus1) ||
+ __builtin_add_overflow(
+ pic_height_in_map_units_minus1, 16, &pic_height_in_map_units_minus1) ||
+ __builtin_mul_overflow(
+ pic_height_in_map_units_minus1, (2 - frame_mbs_only_flag), height)) {
+ *height = 0;
+ }
if (!frame_mbs_only_flag) {
br.getBits(1); // mb_adaptive_frame_field_flag
@@ -202,17 +213,19 @@
// *width -= (frame_crop_left_offset + frame_crop_right_offset) * cropUnitX;
- if(__builtin_add_overflow(frame_crop_left_offset, frame_crop_right_offset, &frame_crop_left_offset) ||
- __builtin_mul_overflow(frame_crop_left_offset, cropUnitX, &frame_crop_left_offset) ||
- __builtin_sub_overflow(*width, frame_crop_left_offset, width) ||
+ if(__builtin_add_overflow(
+ frame_crop_left_offset, frame_crop_right_offset, &frame_crop_left_offset) ||
+ __builtin_mul_overflow(frame_crop_left_offset, cropUnitX, &frame_crop_left_offset) ||
+ __builtin_sub_overflow(*width, frame_crop_left_offset, width) ||
*width < 0) {
*width = 0;
}
//*height -= (frame_crop_top_offset + frame_crop_bottom_offset) * cropUnitY;
- if(__builtin_add_overflow(frame_crop_top_offset, frame_crop_bottom_offset, &frame_crop_top_offset) ||
- __builtin_mul_overflow(frame_crop_top_offset, cropUnitY, &frame_crop_top_offset) ||
- __builtin_sub_overflow(*height, frame_crop_top_offset, height) ||
+ if(__builtin_add_overflow(
+ frame_crop_top_offset, frame_crop_bottom_offset, &frame_crop_top_offset) ||
+ __builtin_mul_overflow(frame_crop_top_offset, cropUnitY, &frame_crop_top_offset) ||
+ __builtin_sub_overflow(*height, frame_crop_top_offset, height) ||
*height < 0) {
*height = 0;
}
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/AMessage.h b/media/libstagefright/foundation/include/media/stagefright/foundation/AMessage.h
index 742651e..b5d6666 100644
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/AMessage.h
+++ b/media/libstagefright/foundation/include/media/stagefright/foundation/AMessage.h
@@ -63,6 +63,7 @@
AMessage();
AMessage(uint32_t what, const sp<const AHandler> &handler);
+#ifndef __ANDROID_VNDK__
// Construct an AMessage from a parcel.
// nestingAllowed determines how many levels AMessage can be nested inside
// AMessage. The default value here is arbitrarily set to 255.
@@ -87,6 +88,7 @@
// All items in the AMessage must have types that are recognized by
// FromParcel(); otherwise, TRESPASS error will occur.
void writeToParcel(Parcel *parcel) const;
+#endif
void setWhat(uint32_t what);
uint32_t what() const;
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/AString.h b/media/libstagefright/foundation/include/media/stagefright/foundation/AString.h
index 0f6299c..deef0d4 100644
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/AString.h
+++ b/media/libstagefright/foundation/include/media/stagefright/foundation/AString.h
@@ -89,8 +89,10 @@
void tolower();
+#ifndef __ANDROID_VNDK__
static AString FromParcel(const Parcel &parcel);
status_t writeToParcel(Parcel *parcel) const;
+#endif
private:
constexpr static const char *kEmptyString = "";
diff --git a/media/libstagefright/foundation/tests/Android.bp b/media/libstagefright/foundation/tests/Android.bp
new file mode 100644
index 0000000..f2157c9
--- /dev/null
+++ b/media/libstagefright/foundation/tests/Android.bp
@@ -0,0 +1,27 @@
+cc_test {
+ name: "sf_foundation_test",
+ test_suites: ["device-tests"],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ include_dirs: [
+ "frameworks/av/include",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libstagefright_foundation",
+ "libutils",
+ ],
+
+ srcs: [
+ "AData_test.cpp",
+ "Base64_test.cpp",
+ "Flagged_test.cpp",
+ "TypeTraits_test.cpp",
+ "Utils_test.cpp",
+ ],
+}
diff --git a/media/libstagefright/foundation/tests/Android.mk b/media/libstagefright/foundation/tests/Android.mk
deleted file mode 100644
index a9e3c76..0000000
--- a/media/libstagefright/foundation/tests/Android.mk
+++ /dev/null
@@ -1,37 +0,0 @@
-# Build the unit tests.
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_MODULE := sf_foundation_test
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := \
- AData_test.cpp \
- Base64_test.cpp \
- Flagged_test.cpp \
- TypeTraits_test.cpp \
- Utils_test.cpp \
-
-LOCAL_SHARED_LIBRARIES := \
- liblog \
- libstagefright_foundation \
- libutils \
-
-LOCAL_C_INCLUDES := \
- frameworks/av/include \
-
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CLANG := true
-
-include $(BUILD_NATIVE_TEST)
-
-# Include subdirectory makefiles
-# ============================================================
-
-# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework
-# team really wants is to build the stuff defined by this makefile.
-ifeq (,$(ONE_SHOT_MAKEFILE))
-include $(call first-makefiles-under,$(LOCAL_PATH))
-endif
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index 4d0848a..fdcde29 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -2161,7 +2161,9 @@
return ERROR_MALFORMED;
}
- CHECK_LE(offset + aac_frame_length, buffer->size());
+ if (aac_frame_length > buffer->size() - offset) {
+ return ERROR_MALFORMED;
+ }
int64_t unitTimeUs = timeUs + numSamples * 1000000LL / sampleRate;
offset += aac_frame_length;
diff --git a/media/libstagefright/include/ACodecBufferChannel.h b/media/libstagefright/include/ACodecBufferChannel.h
index 3a087d1..5f65575 100644
--- a/media/libstagefright/include/ACodecBufferChannel.h
+++ b/media/libstagefright/include/ACodecBufferChannel.h
@@ -67,6 +67,9 @@
virtual ~ACodecBufferChannel();
// BufferChannelBase interface
+ void setCrypto(const sp<ICrypto> &crypto) override;
+ void setDescrambler(const sp<IDescrambler> &descrambler) override;
+
virtual status_t queueInputBuffer(const sp<MediaCodecBuffer> &buffer) override;
virtual status_t queueSecureInputBuffer(
const sp<MediaCodecBuffer> &buffer,
@@ -135,6 +138,9 @@
sp<MemoryDealer> makeMemoryDealer(size_t heapSize);
+ sp<ICrypto> mCrypto;
+ sp<IDescrambler> mDescrambler;
+
bool hasCryptoOrDescrambler() {
return mCrypto != NULL || mDescrambler != NULL;
}
diff --git a/media/libstagefright/include/FrameCaptureLayer.h b/media/libstagefright/include/FrameCaptureLayer.h
new file mode 100644
index 0000000..23fd5e5
--- /dev/null
+++ b/media/libstagefright/include/FrameCaptureLayer.h
@@ -0,0 +1,77 @@
+/*
+ * 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 FRAME_CAPTURE_LAYER_H_
+#define FRAME_CAPTURE_LAYER_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <gui/IConsumerListener.h>
+#include <ui/GraphicTypes.h>
+#include <utils/Mutex.h>
+#include <utils/Condition.h>
+
+namespace android {
+
+class GraphicBuffer;
+class IGraphicBufferConsumer;
+class Rect;
+class Surface;
+
+/*
+ * This class is a simple BufferQueue consumer implementation to
+ * obtain a decoded buffer output from MediaCodec. The output
+ * buffer is then sent to FrameCaptureProcessor to be converted
+ * to sRGB properly.
+ */
+struct FrameCaptureLayer : public ConsumerListener {
+ FrameCaptureLayer();
+ ~FrameCaptureLayer() = default;
+
+ // ConsumerListener
+ void onFrameAvailable(const BufferItem& /*item*/) override;
+ void onBuffersReleased() override;
+ void onSidebandStreamChanged() override;
+
+ status_t init();
+
+ sp<Surface> getSurface() { return mSurface; }
+
+ status_t capture(const ui::PixelFormat reqPixelFormat,
+ const Rect &sourceCrop, sp<GraphicBuffer> *outBuffer);
+
+private:
+ struct BufferLayer;
+ // Note: do not hold any sp ref to GraphicBufferSource
+ // GraphicBufferSource is holding an sp to us, holding any sp ref
+ // to GraphicBufferSource will cause circular dependency and both
+ // object will not be released.
+ sp<IGraphicBufferConsumer> mConsumer;
+ sp<Surface> mSurface;
+ std::map<int32_t, sp<GraphicBuffer> > mSlotToBufferMap;
+
+ Mutex mLock;
+ Condition mCondition;
+ bool mFrameAvailable GUARDED_BY(mLock);
+
+ status_t acquireBuffer(BufferItem *bi);
+ status_t releaseBuffer(const BufferItem &bi);
+
+ DISALLOW_EVIL_CONSTRUCTORS(FrameCaptureLayer);
+};
+
+} // namespace android
+
+#endif // FRAME_CAPTURE_LAYER_H_
diff --git a/media/libstagefright/include/FrameDecoder.h b/media/libstagefright/include/FrameDecoder.h
index dc58c15..19ae0e3 100644
--- a/media/libstagefright/include/FrameDecoder.h
+++ b/media/libstagefright/include/FrameDecoder.h
@@ -22,17 +22,18 @@
#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 <system/graphics-base.h>
+#include <ui/GraphicTypes.h>
namespace android {
struct AMessage;
-class MediaCodecBuffer;
-class IMediaSource;
-class VideoFrame;
struct MediaCodec;
+class IMediaSource;
+class MediaCodecBuffer;
+class Surface;
+class VideoFrame;
struct FrameRect {
int32_t left, top, right, bottom;
@@ -44,13 +45,10 @@
const sp<MetaData> &trackMeta,
const sp<IMediaSource> &source);
- status_t init(
- int64_t frameTimeUs, size_t numFrames, int option, int colorFormat);
+ status_t init(int64_t frameTimeUs, int option, int colorFormat);
sp<IMemory> extractFrame(FrameRect *rect = NULL);
- status_t extractFrames(std::vector<sp<IMemory> >* frames);
-
static sp<IMemory> getMetadataOnly(
const sp<MetaData> &trackMeta, int colorFormat, bool thumbnail = false);
@@ -59,9 +57,9 @@
virtual sp<AMessage> onGetFormatAndSeekOptions(
int64_t frameTimeUs,
- size_t numFrames,
int seekMode,
- MediaSource::ReadOptions *options) = 0;
+ MediaSource::ReadOptions *options,
+ sp<Surface> *window) = 0;
virtual status_t onExtractRect(FrameRect *rect) = 0;
@@ -79,29 +77,30 @@
sp<MetaData> trackMeta() const { return mTrackMeta; }
OMX_COLOR_FORMATTYPE dstFormat() const { return mDstFormat; }
+ ui::PixelFormat captureFormat() const { return mCaptureFormat; }
int32_t dstBpp() const { return mDstBpp; }
-
- void addFrame(const sp<IMemory> &frame) {
- mFrames.push_back(frame);
- }
+ void setFrame(const sp<IMemory> &frameMem) { mFrameMemory = frameMem; }
private:
AString mComponentName;
sp<MetaData> mTrackMeta;
sp<IMediaSource> mSource;
OMX_COLOR_FORMATTYPE mDstFormat;
+ ui::PixelFormat mCaptureFormat;
int32_t mDstBpp;
- std::vector<sp<IMemory> > mFrames;
+ sp<IMemory> mFrameMemory;
MediaSource::ReadOptions mReadOptions;
sp<MediaCodec> mDecoder;
sp<AMessage> mOutputFormat;
bool mHaveMoreInputs;
bool mFirstSample;
+ sp<Surface> mSurface;
status_t extractInternal();
DISALLOW_EVIL_CONSTRUCTORS(FrameDecoder);
};
+struct FrameCaptureLayer;
struct VideoFrameDecoder : public FrameDecoder {
VideoFrameDecoder(
@@ -112,9 +111,9 @@
protected:
virtual sp<AMessage> onGetFormatAndSeekOptions(
int64_t frameTimeUs,
- size_t numFrames,
int seekMode,
- MediaSource::ReadOptions *options) override;
+ MediaSource::ReadOptions *options,
+ sp<Surface> *window) override;
virtual status_t onExtractRect(FrameRect *rect) override {
// Rect extraction for sequences is not supported for now.
@@ -134,11 +133,16 @@
bool *done) override;
private:
+ sp<FrameCaptureLayer> mCaptureLayer;
+ VideoFrame *mFrame;
bool mIsAvcOrHevc;
MediaSource::ReadOptions::SeekMode mSeekMode;
int64_t mTargetTimeUs;
- size_t mNumFrames;
- size_t mNumFramesDecoded;
+ List<int64_t> mSampleDurations;
+ int64_t mDefaultSampleDurationUs;
+
+ sp<Surface> initSurface();
+ status_t captureSurface();
};
struct ImageDecoder : public FrameDecoder {
@@ -150,9 +154,9 @@
protected:
virtual sp<AMessage> onGetFormatAndSeekOptions(
int64_t frameTimeUs,
- size_t numFrames,
int seekMode,
- MediaSource::ReadOptions *options) override;
+ MediaSource::ReadOptions *options,
+ sp<Surface> *window) override;
virtual status_t onExtractRect(FrameRect *rect) override;
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 784fd36..7754de4 100644
--- a/media/libstagefright/include/media/stagefright/ACodec.h
+++ b/media/libstagefright/include/media/stagefright/ACodec.h
@@ -37,6 +37,15 @@
#define TRACK_BUFFER_TIMING 0
namespace android {
+namespace hardware {
+namespace media {
+namespace omx {
+namespace V1_0 {
+struct IGraphicBufferSource;
+} // namespace V1_0
+} // namespace omx
+} // namespace media
+} // namespace hardware
struct ABuffer;
class ACodecBufferChannel;
@@ -279,7 +288,7 @@
size_t mNumUndequeuedBuffers;
sp<DataConverter> mConverter[2];
- sp<IGraphicBufferSource> mGraphicBufferSource;
+ sp<hardware::media::omx::V1_0::IGraphicBufferSource> mGraphicBufferSource;
int64_t mRepeatFrameDelayUs;
int64_t mMaxPtsGapUs;
float mMaxFps;
@@ -496,6 +505,7 @@
AudioEncoding encoding = kAudioEncodingPcm16bit);
status_t setPriority(int32_t priority);
+ status_t setLowLatency(int32_t lowLatency);
status_t setLatency(uint32_t latency);
status_t getLatency(uint32_t *latency);
status_t setAudioPresentation(int32_t presentationId, int32_t programId);
diff --git a/media/libstagefright/include/media/stagefright/AudioSource.h b/media/libstagefright/include/media/stagefright/AudioSource.h
index af04dad..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>
@@ -37,7 +37,7 @@
// Note that the "channels" parameter _is_ the number of channels,
// _not_ a bitmask of audio_channels_t constants.
AudioSource(
- audio_source_t inputSource,
+ const audio_attributes_t *attr,
const String16 &opPackageName,
uint32_t sampleRate,
uint32_t channels,
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 ad60f46..bc7881c 100644
--- a/media/libstagefright/include/media/stagefright/CodecBase.h
+++ b/media/libstagefright/include/media/stagefright/CodecBase.h
@@ -42,12 +42,20 @@
struct RenderedFrameInfo;
class Surface;
struct ICrypto;
+class IMemory;
+
namespace hardware {
namespace cas {
namespace native {
namespace V1_0 {
struct IDescrambler;
-}}}}
+}}}
+namespace drm {
+namespace V1_0 {
+struct SharedBuffer;
+}}
+}
+
using hardware::cas::native::V1_0::IDescrambler;
struct CodecBase : public AHandler, /* static */ ColorUtils {
@@ -249,15 +257,15 @@
*/
class BufferChannelBase {
public:
+ BufferChannelBase() = default;
virtual ~BufferChannelBase() = default;
inline void setCallback(std::unique_ptr<CodecBase::BufferCallback> &&callback) {
mCallback = std::move(callback);
}
- void setCrypto(const sp<ICrypto> &crypto);
-
- void setDescrambler(const sp<IDescrambler> &descrambler);
+ virtual void setCrypto(const sp<ICrypto> &) {}
+ virtual void setDescrambler(const sp<IDescrambler> &) {}
/**
* Queue an input buffer into the buffer channel.
@@ -314,10 +322,20 @@
*/
virtual void getOutputBufferArray(Vector<sp<MediaCodecBuffer>> *array) = 0;
+ /**
+ * Convert binder IMemory to drm SharedBuffer
+ *
+ * \param memory IMemory object to store encrypted content.
+ * \param heapSeqNum Heap sequence number from ICrypto; -1 if N/A
+ * \param buf SharedBuffer structure to fill.
+ */
+ static void IMemoryToSharedBuffer(
+ const sp<IMemory> &memory,
+ int32_t heapSeqNum,
+ hardware::drm::V1_0::SharedBuffer *buf);
+
protected:
std::unique_ptr<CodecBase::BufferCallback> mCallback;
- sp<ICrypto> mCrypto;
- sp<IDescrambler> mDescrambler;
};
} // namespace android
diff --git a/media/libstagefright/include/media/stagefright/FrameCaptureProcessor.h b/media/libstagefright/include/media/stagefright/FrameCaptureProcessor.h
new file mode 100644
index 0000000..66e5daa
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/FrameCaptureProcessor.h
@@ -0,0 +1,98 @@
+/*
+ * 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 FRAME_CAPTURE_PROCESSOR_H_
+#define FRAME_CAPTURE_PROCESSOR_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AHandler.h>
+
+namespace android {
+
+struct AMessage;
+class GraphicBuffer;
+class Rect;
+
+namespace renderengine {
+class RenderEngine;
+struct LayerSettings;
+}
+
+/*
+ * Process a decoded graphic buffer through RenderEngine to
+ * convert it to sRGB.
+ *
+ * This class is a singleton that holds one instance of RenderEngine
+ * and its event queue (on which the GL context runs). The RenderEngine
+ * is created upon the first getInstance().
+ */
+class FrameCaptureProcessor : public AHandler {
+
+public:
+
+ struct Layer : public RefBase {
+ virtual void getLayerSettings(
+ const Rect &sourceCrop, uint32_t textureName,
+ renderengine::LayerSettings *layerSettings) = 0;
+ };
+
+ static sp<FrameCaptureProcessor> getInstance();
+
+ status_t capture(
+ const sp<Layer> &layer,
+ const Rect &sourceCrop, const sp<GraphicBuffer> &outBuffer);
+
+protected:
+ virtual ~FrameCaptureProcessor();
+ void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+ FrameCaptureProcessor();
+
+ enum {
+ kWhatCreate,
+ kWhatCapture,
+ };
+
+ static Mutex sLock;
+ static sp<FrameCaptureProcessor> sInstance GUARDED_BY(sLock);
+
+ constexpr static float sDefaultMaxLumiance = 500.0f;
+
+ status_t mInitStatus;
+ sp<ALooper> mLooper;
+ std::unique_ptr<renderengine::RenderEngine> mRE;
+ uint32_t mTextureName;
+
+ static status_t PostAndAwaitResponse(
+ const sp<AMessage> &msg, sp<AMessage> *response);
+ static void PostReplyWithError(
+ const sp<AReplyToken> &replyID, status_t err);
+
+ status_t initCheck() { return mInitStatus; }
+ void createRenderEngine();
+
+ // message handlers
+ status_t onCreate();
+ status_t onCapture(const sp<Layer> &layer,
+ const Rect &sourceCrop, const sp<GraphicBuffer> &outBuffer);
+
+ DISALLOW_EVIL_CONSTRUCTORS(FrameCaptureProcessor);
+};
+
+} // namespace android
+
+#endif // FRAME_CAPTURE_PROCESSOR_H_
diff --git a/media/libstagefright/include/media/stagefright/InterfaceUtils.h b/media/libstagefright/include/media/stagefright/InterfaceUtils.h
index b83a958..92ef543 100644
--- a/media/libstagefright/include/media/stagefright/InterfaceUtils.h
+++ b/media/libstagefright/include/media/stagefright/InterfaceUtils.h
@@ -19,8 +19,8 @@
#include <utils/RefBase.h>
#include <media/stagefright/RemoteMediaExtractor.h>
-#include <media/MediaSource.h>
-#include <media/IMediaExtractor.h>
+#include <media/stagefright/MediaSource.h>
+#include <android/IMediaExtractor.h>
#include <media/IMediaSource.h>
namespace android {
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/MediaBuffer.h b/media/libstagefright/include/media/stagefright/MediaBuffer.h
index ace63ae..9145b63 100644
--- a/media/libstagefright/include/media/stagefright/MediaBuffer.h
+++ b/media/libstagefright/include/media/stagefright/MediaBuffer.h
@@ -48,7 +48,11 @@
explicit MediaBuffer(const sp<ABuffer> &buffer);
#ifndef NO_IMEMORY
MediaBuffer(const sp<IMemory> &mem) :
- MediaBuffer((uint8_t *)mem->pointer() + sizeof(SharedControl), mem->size()) {
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ MediaBuffer((uint8_t *)mem->unsecurePointer() + sizeof(SharedControl), mem->size()) {
// delegate and override mMemory
mMemory = mem;
}
@@ -94,9 +98,13 @@
virtual int remoteRefcount() const {
#ifndef NO_IMEMORY
- if (mMemory.get() == nullptr || mMemory->pointer() == nullptr) return 0;
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ if (mMemory.get() == nullptr || mMemory->unsecurePointer() == nullptr) return 0;
int32_t remoteRefcount =
- reinterpret_cast<SharedControl *>(mMemory->pointer())->getRemoteRefcount();
+ reinterpret_cast<SharedControl *>(mMemory->unsecurePointer())->getRemoteRefcount();
// Sanity check so that remoteRefCount() is non-negative.
return remoteRefcount >= 0 ? remoteRefcount : 0; // do not allow corrupted data.
#else
@@ -107,8 +115,12 @@
// returns old value
int addRemoteRefcount(int32_t value) {
#ifndef NO_IMEMORY
- if (mMemory.get() == nullptr || mMemory->pointer() == nullptr) return 0;
- return reinterpret_cast<SharedControl *>(mMemory->pointer())->addRemoteRefcount(value);
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ if (mMemory.get() == nullptr || mMemory->unsecurePointer() == nullptr) return 0;
+ return reinterpret_cast<SharedControl *>(mMemory->unsecurePointer())->addRemoteRefcount(value);
#else
(void) value;
return 0;
@@ -121,8 +133,12 @@
static bool isDeadObject(const sp<IMemory> &memory) {
#ifndef NO_IMEMORY
- if (memory.get() == nullptr || memory->pointer() == nullptr) return false;
- return reinterpret_cast<SharedControl *>(memory->pointer())->isDeadObject();
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ if (memory.get() == nullptr || memory->unsecurePointer() == nullptr) return false;
+ return reinterpret_cast<SharedControl *>(memory->unsecurePointer())->isDeadObject();
#else
(void) memory;
return false;
@@ -220,7 +236,11 @@
inline SharedControl *getSharedControl() const {
#ifndef NO_IMEMORY
- return reinterpret_cast<SharedControl *>(mMemory->pointer());
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ return reinterpret_cast<SharedControl *>(mMemory->unsecurePointer());
#else
return nullptr;
#endif
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index 01d0325..5c5c23a 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -24,12 +24,19 @@
#include <gui/IGraphicBufferProducer.h>
#include <media/hardware/CryptoAPI.h>
#include <media/MediaCodecInfo.h>
-#include <media/MediaResource.h>
#include <media/MediaMetrics.h>
#include <media/stagefright/foundation/AHandler.h>
#include <media/stagefright/FrameRenderTracker.h>
#include <utils/Vector.h>
+namespace aidl {
+namespace android {
+namespace media {
+class MediaResourceParcel;
+} // media
+} // android
+} // aidl
+
namespace android {
struct ABuffer;
@@ -43,8 +50,6 @@
struct ICrypto;
class MediaCodecBuffer;
class IMemory;
-class IResourceManagerClient;
-class IResourceManagerService;
struct PersistentSurface;
class SoftwareRenderer;
class Surface;
@@ -54,7 +59,9 @@
namespace V1_0 {
struct IDescrambler;
}}}}
+
using hardware::cas::native::V1_0::IDescrambler;
+using aidl::android::media::MediaResourceParcel;
struct MediaCodec : public AHandler {
enum ConfigureFlags {
@@ -284,34 +291,7 @@
bool mOwnedByClient;
};
- struct ResourceManagerServiceProxy : public IBinder::DeathRecipient {
- ResourceManagerServiceProxy(pid_t pid, uid_t uid);
- ~ResourceManagerServiceProxy();
-
- void init();
-
- // implements DeathRecipient
- virtual void binderDied(const wp<IBinder>& /*who*/);
-
- void addResource(
- int64_t clientId,
- const sp<IResourceManagerClient> &client,
- const Vector<MediaResource> &resources);
-
- void removeResource(
- int64_t clientId,
- const Vector<MediaResource> &resources);
-
- void removeClient(int64_t clientId);
-
- bool reclaimResource(const Vector<MediaResource> &resources);
-
- private:
- Mutex mLock;
- sp<IResourceManagerService> mService;
- pid_t mPid;
- uid_t mUid;
- };
+ struct ResourceManagerServiceProxy;
State mState;
uid_t mUid;
@@ -333,14 +313,14 @@
void updateMediametrics();
void flushMediametrics();
void updateEphemeralMediametrics(mediametrics_handle_t item);
+ void updateLowLatency(const sp<AMessage> &msg);
sp<AMessage> mOutputFormat;
sp<AMessage> mInputFormat;
sp<AMessage> mCallback;
sp<AMessage> mOnFrameRenderedNotification;
- sp<IResourceManagerClient> mResourceManagerClient;
- sp<ResourceManagerServiceProxy> mResourceManagerService;
+ sp<ResourceManagerServiceProxy> mResourceManagerProxy;
bool mIsVideo;
int32_t mVideoWidth;
@@ -434,8 +414,6 @@
bool isExecuting() const;
uint64_t getGraphicBufferSize();
- void addResource(MediaResource::Type type, MediaResource::SubType subtype, uint64_t value);
- void removeResource(MediaResource::Type type, MediaResource::SubType subtype, uint64_t value);
void requestCpuBoostIfNeeded();
bool hasPendingBuffer(int portIndex);
@@ -463,6 +441,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 50d7724..16e207d 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
@@ -510,22 +510,24 @@
constexpr int32_t DolbyVisionProfileDvheStn = 0x20;
constexpr int32_t DolbyVisionProfileDvheDth = 0x40;
constexpr int32_t DolbyVisionProfileDvheDtb = 0x80;
-constexpr int32_t DolbyVisionProfileDvheSt = 0x100;
-constexpr int32_t DolbyVisionProfileDvavSe = 0x200;
+constexpr int32_t DolbyVisionProfileDvheSt = 0x100;
+constexpr int32_t DolbyVisionProfileDvavSe = 0x200;
+constexpr int32_t DolbyVisionProfileDvav110 = 0x400;
inline static const char *asString_DolbyVisionProfile(int32_t i, const char *def = "??") {
switch (i) {
- case DolbyVisionProfileDvavPer: return "DvavPer";
- case DolbyVisionProfileDvavPen: return "DvavPen";
- case DolbyVisionProfileDvheDer: return "DvheDer";
- case DolbyVisionProfileDvheDen: return "DvheDen";
- case DolbyVisionProfileDvheDtr: return "DvheDtr";
- case DolbyVisionProfileDvheStn: return "DvheStn";
- case DolbyVisionProfileDvheDth: return "DvheDth";
- case DolbyVisionProfileDvheDtb: return "DvheDtb";
- case DolbyVisionProfileDvheSt: return "DvheSt";
- case DolbyVisionProfileDvavSe: return "DvavSe";
- default: return def;
+ case DolbyVisionProfileDvavPer: return "DvavPer";
+ case DolbyVisionProfileDvavPen: return "DvavPen";
+ case DolbyVisionProfileDvheDer: return "DvheDer";
+ case DolbyVisionProfileDvheDen: return "DvheDen";
+ case DolbyVisionProfileDvheDtr: return "DvheDtr";
+ case DolbyVisionProfileDvheStn: return "DvheStn";
+ case DolbyVisionProfileDvheDth: return "DvheDth";
+ case DolbyVisionProfileDvheDtb: return "DvheDtb";
+ case DolbyVisionProfileDvheSt: return "DvheSt";
+ case DolbyVisionProfileDvavSe: return "DvavSe";
+ case DolbyVisionProfileDvav110: return "Dav110";
+ default: return def;
}
}
@@ -774,6 +776,7 @@
constexpr char KEY_LANGUAGE[] = "language";
constexpr char KEY_LATENCY[] = "latency";
constexpr char KEY_LEVEL[] = "level";
+constexpr char KEY_LOW_LATENCY[] = "low-latency";
constexpr char KEY_MAX_B_FRAMES[] = "max-bframes";
constexpr char KEY_MAX_BIT_RATE[] = "max-bitrate";
constexpr char KEY_MAX_FPS_TO_ENCODER[] = "max-fps-to-encoder";
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/MediaExtractorFactory.h b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h
index 2ab98e1..745e342 100644
--- a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h
+++ b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h
@@ -22,7 +22,7 @@
#include <unordered_set>
#include <android/dlext.h>
-#include <media/IMediaExtractor.h>
+#include <android/IMediaExtractor.h>
namespace android {
@@ -36,7 +36,7 @@
static sp<IMediaExtractor> CreateFromService(
const sp<DataSource> &source, const char *mime = NULL);
static status_t dump(int fd, const Vector<String16>& args);
- static std::unordered_set<std::string> getSupportedTypes();
+ static std::vector<std::string> getSupportedTypes();
static void LoadExtractors();
private:
diff --git a/media/libstagefright/include/media/stagefright/MediaFilter.h b/media/libstagefright/include/media/stagefright/MediaFilter.h
index a28c49d..1255e0f 100644
--- a/media/libstagefright/include/media/stagefright/MediaFilter.h
+++ b/media/libstagefright/include/media/stagefright/MediaFilter.h
@@ -21,9 +21,7 @@
namespace android {
-class ACodecBufferChannel;
struct GraphicBufferListener;
-class MemoryDealer;
struct SimpleFilter;
struct MediaFilter : public CodecBase {
@@ -65,6 +63,8 @@
sp<MediaCodecBuffer> mData;
};
+ class BufferChannel;
+
enum State {
UNINITIALIZED,
INITIALIZED,
@@ -104,7 +104,6 @@
sp<AMessage> mInputFormat;
sp<AMessage> mOutputFormat;
- sp<MemoryDealer> mDealer[2];
Vector<BufferInfo> mBuffers[2];
Vector<BufferInfo*> mAvailableInputBuffers;
Vector<BufferInfo*> mAvailableOutputBuffers;
@@ -113,15 +112,15 @@
sp<SimpleFilter> mFilter;
sp<GraphicBufferListener> mGraphicBufferListener;
- std::shared_ptr<ACodecBufferChannel> mBufferChannel;
+ std::shared_ptr<BufferChannel> mBufferChannel;
// helper functions
void signalProcessBuffers();
void signalError(status_t error);
status_t allocateBuffersOnPort(OMX_U32 portIndex);
- BufferInfo *findBufferByID(
- uint32_t portIndex, uint32_t bufferID,
+ BufferInfo *findBuffer(
+ uint32_t portIndex, const sp<MediaCodecBuffer> &buffer,
ssize_t *index = NULL);
void postFillThisBuffer(BufferInfo *info);
void postDrainThisBuffer(BufferInfo *info);
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/MetaData.h b/media/libstagefright/include/media/stagefright/MetaData.h
index f625358..68adf346 100644
--- a/media/libstagefright/include/media/stagefright/MetaData.h
+++ b/media/libstagefright/include/media/stagefright/MetaData.h
@@ -41,7 +41,9 @@
friend class BnMediaSource;
friend class BpMediaSource;
friend class BpMediaExtractor;
+#ifndef __ANDROID_VNDK__
static sp<MetaData> createFromParcel(const Parcel &parcel);
+#endif
};
} // namespace android
diff --git a/media/libstagefright/include/media/stagefright/MetaDataBase.h b/media/libstagefright/include/media/stagefright/MetaDataBase.h
index 8dc2dd5..b9cd18a 100644
--- a/media/libstagefright/include/media/stagefright/MetaDataBase.h
+++ b/media/libstagefright/include/media/stagefright/MetaDataBase.h
@@ -59,6 +59,7 @@
kKeyAACProfile = 'aacp', // int32_t
kKeyAVCC = 'avcc', // raw data
kKeyHVCC = 'hvcc', // raw data
+ kKeyDVCC = 'dvcc', // raw data
kKeyAV1C = 'av1c', // raw data
kKeyThumbnailHVCC = 'thvc', // raw data
kKeyD263 = 'd263', // raw data
@@ -112,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
@@ -245,6 +244,7 @@
kTypeAVCC = 'avcc',
kTypeHVCC = 'hvcc',
kTypeAV1C = 'av1c',
+ kTypeDVCC = 'dvcc',
kTypeD263 = 'd263',
};
@@ -319,8 +319,10 @@
struct Rect;
struct MetaDataInternal;
MetaDataInternal *mInternalData;
+#ifndef __ANDROID_VNDK__
status_t writeToParcel(Parcel &parcel);
status_t updateFromParcel(const Parcel &parcel);
+#endif
};
} // namespace android
diff --git a/media/libstagefright/include/media/stagefright/NuMediaExtractor.h b/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
index 4307110..227cead 100644
--- a/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
+++ b/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
@@ -21,8 +21,8 @@
#include <media/mediaplayer.h>
#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/foundation/AudioPresentationInfo.h>
-#include <media/IMediaExtractor.h>
-#include <media/MediaSource.h>
+#include <android/IMediaExtractor.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/PersistentSurface.h b/media/libstagefright/include/media/stagefright/PersistentSurface.h
index 49b36c9..f4943c3 100644
--- a/media/libstagefright/include/media/stagefright/PersistentSurface.h
+++ b/media/libstagefright/include/media/stagefright/PersistentSurface.h
@@ -18,31 +18,21 @@
#define PERSISTENT_SURFACE_H_
-#include <android/IGraphicBufferSource.h>
#include <binder/Parcel.h>
#include <hidl/HidlSupport.h>
#include <hidl/HybridInterface.h>
#include <gui/IGraphicBufferProducer.h>
#include <media/stagefright/foundation/ABase.h>
-using android::hidl::base::V1_0::IBase;
-
namespace android {
struct PersistentSurface : public RefBase {
PersistentSurface() {}
- // create an OMX persistent surface
+ // create a persistent surface
PersistentSurface(
const sp<IGraphicBufferProducer>& bufferProducer,
- const sp<IGraphicBufferSource>& bufferSource) :
- mBufferProducer(bufferProducer),
- mBufferSource(bufferSource) { }
-
- // create a HIDL persistent surface
- PersistentSurface(
- const sp<IGraphicBufferProducer>& bufferProducer,
- const sp<IBase>& hidlTarget) :
+ const sp<hidl::base::V1_0::IBase>& hidlTarget) :
mBufferProducer(bufferProducer),
mHidlTarget(hidlTarget) { }
@@ -50,18 +40,12 @@
return mBufferProducer;
}
- sp<IGraphicBufferSource> getBufferSource() const {
- return mBufferSource;
- }
-
- sp<IBase> getHidlTarget() const {
+ sp<hidl::base::V1_0::IBase> getHidlTarget() const {
return mHidlTarget;
}
status_t writeToParcel(Parcel *parcel) const {
parcel->writeStrongBinder(IInterface::asBinder(mBufferProducer));
- // this can handle null
- parcel->writeStrongBinder(IInterface::asBinder(mBufferSource));
// write hidl target
if (mHidlTarget != nullptr) {
HalToken token;
@@ -79,8 +63,6 @@
status_t readFromParcel(const Parcel *parcel) {
mBufferProducer = interface_cast<IGraphicBufferProducer>(
parcel->readStrongBinder());
- mBufferSource = interface_cast<IGraphicBufferSource>(
- parcel->readStrongBinder());
// read hidl target
bool haveHidlTarget = parcel->readBool();
if (haveHidlTarget) {
@@ -97,8 +79,7 @@
private:
sp<IGraphicBufferProducer> mBufferProducer;
- sp<IGraphicBufferSource> mBufferSource;
- sp<IBase> mHidlTarget;
+ sp<hidl::base::V1_0::IBase> mHidlTarget;
DISALLOW_EVIL_CONSTRUCTORS(PersistentSurface);
};
diff --git a/media/libstagefright/include/media/stagefright/RemoteDataSource.h b/media/libstagefright/include/media/stagefright/RemoteDataSource.h
index 5a69bd7..d82be8a 100644
--- a/media/libstagefright/include/media/stagefright/RemoteDataSource.h
+++ b/media/libstagefright/include/media/stagefright/RemoteDataSource.h
@@ -17,10 +17,10 @@
#ifndef REMOTE_DATA_SOURCE_H_
#define REMOTE_DATA_SOURCE_H_
+#include <android/IDataSource.h>
#include <binder/IMemory.h>
#include <binder/MemoryDealer.h>
#include <media/DataSource.h>
-#include <media/IDataSource.h>
namespace android {
@@ -48,7 +48,7 @@
if (size > kBufferSize) {
size = kBufferSize;
}
- return mSource->readAt(offset, mMemory->pointer(), size);
+ return mSource->readAt(offset, mMemory->unsecurePointer(), size);
}
virtual status_t getSize(off64_t *size) {
return mSource->getSize(size);
diff --git a/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h b/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h
index 9925114..e33e303 100644
--- a/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h
+++ b/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h
@@ -17,13 +17,13 @@
#ifndef REMOTE_MEDIA_EXTRACTOR_H_
#define REMOTE_MEDIA_EXTRACTOR_H_
-#include <media/IMediaExtractor.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 {
@@ -48,7 +48,7 @@
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/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/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 5af7b23..657144c 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -36,6 +36,10 @@
#include <inttypes.h>
#include <netinet/in.h>
+#ifndef __ANDROID_APEX__
+#include "HlsSampleDecryptor.h"
+#endif
+
namespace android {
ElementaryStreamQueue::ElementaryStreamQueue(Mode mode, uint32_t flags)
@@ -50,7 +54,13 @@
// Create the decryptor anyway since we don't know the use-case unless key is provided
// Won't decrypt if key info not available (e.g., scanner/extractor just parsing ts files)
- mSampleDecryptor = isSampleEncrypted() ? new HlsSampleDecryptor : NULL;
+ mSampleDecryptor = isSampleEncrypted() ?
+#ifdef __ANDROID_APEX__
+ new SampleDecryptor
+#else
+ new HlsSampleDecryptor
+#endif
+ : NULL;
}
sp<MetaData> ElementaryStreamQueue::getFormat() {
diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h
index 3227f47..a06bd6a 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.h
+++ b/media/libstagefright/mpeg2ts/ESQueue.h
@@ -25,7 +25,7 @@
#include <utils/RefBase.h>
#include <vector>
-#include "HlsSampleDecryptor.h"
+#include "SampleDecryptor.h"
namespace android {
@@ -109,7 +109,7 @@
sp<MetaData> mFormat;
- sp<HlsSampleDecryptor> mSampleDecryptor;
+ sp<SampleDecryptor> mSampleDecryptor;
int mAUIndex;
bool isSampleEncrypted() const {
diff --git a/media/libstagefright/mpeg2ts/HlsSampleDecryptor.h b/media/libstagefright/mpeg2ts/HlsSampleDecryptor.h
index 2c76620..63b4d7b 100644
--- a/media/libstagefright/mpeg2ts/HlsSampleDecryptor.h
+++ b/media/libstagefright/mpeg2ts/HlsSampleDecryptor.h
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-#ifndef SAMPLE_AES_PROCESSOR_H_
+#ifndef HLS_SAMPLE_AES_PROCESSOR_H_
-#define SAMPLE_AES_PROCESSOR_H_
+#define HLS_SAMPLE_AES_PROCESSOR_H_
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/AString.h>
@@ -28,18 +28,20 @@
#include <utils/RefBase.h>
#include <utils/Vector.h>
+#include "SampleDecryptor.h"
+
namespace android {
-struct HlsSampleDecryptor : RefBase {
+struct HlsSampleDecryptor : SampleDecryptor {
HlsSampleDecryptor();
explicit HlsSampleDecryptor(const sp<AMessage> &sampleAesKeyItem);
- void signalNewSampleAesKey(const sp<AMessage> &sampleAesKeyItem);
+ virtual void signalNewSampleAesKey(const sp<AMessage> &sampleAesKeyItem);
- size_t processNal(uint8_t *nalData, size_t nalSize);
- void processAAC(size_t adtsHdrSize, uint8_t *data, size_t size);
- void processAC3(uint8_t *data, size_t size);
+ virtual size_t processNal(uint8_t *nalData, size_t nalSize);
+ virtual void processAAC(size_t adtsHdrSize, uint8_t *data, size_t size);
+ virtual void processAC3(uint8_t *data, size_t size);
static AString aesBlockToStr(uint8_t block[AES_BLOCK_SIZE]);
@@ -60,4 +62,4 @@
} // namespace android
-#endif // SAMPLE_AES_PROCESSOR_H_
+#endif // HLS_SAMPLE_AES_PROCESSOR_H_
diff --git a/media/libstagefright/mpeg2ts/SampleDecryptor.h b/media/libstagefright/mpeg2ts/SampleDecryptor.h
new file mode 100644
index 0000000..6bc4ac8
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/SampleDecryptor.h
@@ -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.
+ */
+
+#ifndef SAMPLE_AES_PROCESSOR_H_
+
+#define SAMPLE_AES_PROCESSOR_H_
+
+#include <media/stagefright/foundation/AMessage.h>
+
+#include <utils/RefBase.h>
+
+namespace android {
+
+// Base class of HlsSampleDecryptor which has dummy default implementation.
+struct SampleDecryptor : RefBase {
+
+ SampleDecryptor() { };
+
+ virtual void signalNewSampleAesKey(const sp<AMessage> &) { };
+
+ virtual size_t processNal(uint8_t *, size_t) { return -1; };
+ virtual void processAAC(size_t, uint8_t *, size_t) { };
+ virtual void processAC3(uint8_t *, size_t) { };
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(SampleDecryptor);
+};
+
+} // namespace android
+
+#endif // SAMPLE_AES_PROCESSOR_H_
diff --git a/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp b/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp
index ed272bb..7d217eb 100644
--- a/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp
+++ b/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+#ifdef __LP64__
+#define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS
+#endif
+
//#define LOG_NDEBUG 0
#define LOG_TAG "TWGraphicBufferSource"
@@ -21,6 +25,7 @@
#include <media/stagefright/omx/1.0/WOmxNode.h>
#include <media/stagefright/omx/1.0/Conversion.h>
#include <media/stagefright/omx/OMXUtils.h>
+#include <media/stagefright/omx/OmxGraphicBufferSource.h>
#include <android/hardware/media/omx/1.0/IOmxBufferSource.h>
#include <android/hardware/media/omx/1.0/IOmxNode.h>
#include <media/openmax/OMX_Component.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 ddb4ba0..e1c3916 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -128,7 +128,7 @@
}
OMX_U8 *getPointer() {
- return mMem.get() ? static_cast<OMX_U8*>(mMem->pointer()) :
+ return mMem.get() ? static_cast<OMX_U8*>(mMem->unsecurePointer()) :
mHidlMemory.get() ? static_cast<OMX_U8*>(
static_cast<void*>(mHidlMemory->getPointer())) : nullptr;
}
@@ -1173,7 +1173,11 @@
return BAD_VALUE;
}
if (params != NULL) {
- paramsPointer = params->pointer();
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ paramsPointer = params->unsecurePointer();
paramsSize = params->size();
} else if (hParams != NULL) {
paramsPointer = hParams->getPointer();
diff --git a/media/libstagefright/omx/OmxGraphicBufferSource.cpp b/media/libstagefright/omx/OmxGraphicBufferSource.cpp
index 8de1f4f..7b187f9 100644
--- a/media/libstagefright/omx/OmxGraphicBufferSource.cpp
+++ b/media/libstagefright/omx/OmxGraphicBufferSource.cpp
@@ -14,12 +14,18 @@
* limitations under the License.
*/
+#ifdef __LP64__
+#define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS
+#endif
+
#include <inttypes.h>
#define LOG_TAG "OmxGraphicBufferSource"
//#define LOG_NDEBUG 0
#include <utils/Log.h>
+#include <media/openmax/OMX_Core.h>
+
#include <media/stagefright/bqhelper/ComponentWrapper.h>
#include <media/stagefright/bqhelper/GraphicBufferSource.h>
#include <media/stagefright/omx/OmxGraphicBufferSource.h>
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h b/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h
index 9669677..264c01d 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h
@@ -48,7 +48,6 @@
#include <android/hardware/media/omx/1.0/IGraphicBufferSource.h>
#include <android/hardware/media/omx/1.0/IOmxObserver.h>
-#include <android/IGraphicBufferSource.h>
#include <android/IOMXBufferSource.h>
namespace android {
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/1.0/WGraphicBufferSource.h b/media/libstagefright/omx/include/media/stagefright/omx/1.0/WGraphicBufferSource.h
index 4e56c98..02d4b7b 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/1.0/WGraphicBufferSource.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/1.0/WGraphicBufferSource.h
@@ -26,18 +26,16 @@
#include <android/hardware/media/omx/1.0/IOmxNode.h>
#include <android/hardware/media/omx/1.0/IGraphicBufferSource.h>
-#include <android/BnGraphicBufferSource.h>
-
-#include <media/stagefright/omx/OmxGraphicBufferSource.h>
-
namespace android {
+
+class OmxGraphicBufferSource;
+
namespace hardware {
namespace media {
namespace omx {
namespace V1_0 {
namespace implementation {
-using ::android::OmxGraphicBufferSource;
using ::android::hardware::graphics::common::V1_0::Dataspace;
using ::android::hardware::media::omx::V1_0::ColorAspects;
using ::android::hardware::media::omx::V1_0::IGraphicBufferSource;
@@ -52,8 +50,6 @@
using ::android::hardware::Void;
using ::android::sp;
-using ::android::IOMXNode;
-
/**
* Wrapper classes for conversion
* ==============================
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/OmxGraphicBufferSource.h b/media/libstagefright/omx/include/media/stagefright/omx/OmxGraphicBufferSource.h
index 518e0cb..e576d75 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/OmxGraphicBufferSource.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/OmxGraphicBufferSource.h
@@ -21,7 +21,6 @@
#include <media/stagefright/bqhelper/GraphicBufferSource.h>
#include <media/stagefright/foundation/ABase.h>
-#include <android/BnGraphicBufferSource.h>
#include <android/BnOMXBufferSource.h>
#include "IOmxNodeWrapper.h"
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/ARTSPConnection.h b/media/libstagefright/rtsp/ARTSPConnection.h
index 8df2676..56b604d 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.h
+++ b/media/libstagefright/rtsp/ARTSPConnection.h
@@ -46,6 +46,8 @@
const char *url, AString *host, unsigned *port, AString *path,
AString *user, AString *pass);
+ int getSocket() { return mSocket; }
+
protected:
virtual ~ARTSPConnection();
virtual void onMessageReceived(const sp<AMessage> &msg);
diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp
index 9263565..2b42040 100644
--- a/media/libstagefright/rtsp/ASessionDescription.cpp
+++ b/media/libstagefright/rtsp/ASessionDescription.cpp
@@ -141,6 +141,12 @@
AString key, value;
ssize_t equalPos = line.find("=");
+ /* The condition 'if (line.size() < 2 || line.c_str()[1] != '=')' a few lines above
+ * ensures '=' is at position 1. However for robustness we do the following check.
+ */
+ if (equalPos < 0) {
+ return false;
+ }
key = AString(line, 0, equalPos + 1);
value = AString(line, equalPos + 1, line.size() - equalPos - 1);
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 9c30623..7f025a5 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -258,6 +258,10 @@
msg->post();
}
+ sp<ARTSPConnection> getARTSPConnection() {
+ return mConn;
+ }
+
static void addRR(const sp<ABuffer> &buf) {
uint8_t *ptr = buf->data() + buf->size();
ptr[0] = 0x80 | 0;
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/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/mediaserver/manifest_media_c2_software.xml b/media/mediaserver/manifest_media_c2_software.xml
index 5196336..f23ed44 100644
--- a/media/mediaserver/manifest_media_c2_software.xml
+++ b/media/mediaserver/manifest_media_c2_software.xml
@@ -2,7 +2,7 @@
<hal>
<name>android.hardware.media.c2</name>
<transport>hwbinder</transport>
- <version>1.0</version>
+ <version>1.1</version>
<interface>
<name>IComponentStore</name>
<instance>software</instance>
diff --git a/media/mediaserver/mediaserver.rc b/media/mediaserver/mediaserver.rc
index f6c325c..ecb75a9 100644
--- a/media/mediaserver/mediaserver.rc
+++ b/media/mediaserver/mediaserver.rc
@@ -1,3 +1,6 @@
+on property:init.svc.media=*
+ setprop init.svc.mediadrm ${init.svc.media}
+
service media /system/bin/mediaserver
class main
user media
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index a291939..fdf51b1 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -45,6 +45,7 @@
#include "android-base/strings.h"
namespace android {
+static const int SN_EVENT_LOG_ID = 0x534e4554;
static const MtpOperationCode kSupportedOperationCodes[] = {
MTP_OPERATION_GET_DEVICE_INFO,
@@ -83,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,
@@ -967,9 +968,20 @@
if (!parseDateTime(modified, modifiedTime))
modifiedTime = 0;
+ if ((strcmp(name, ".") == 0) || (strcmp(name, "..") == 0) ||
+ (strcmp(name, "/") == 0) || (strcmp(basename(name), name) != 0)) {
+ char errMsg[80];
+
+ snprintf(errMsg, sizeof(errMsg), "Invalid name: %s", (const char *) name);
+ ALOGE("%s (b/130656917)", errMsg);
+ android_errorWriteWithInfoLog(SN_EVENT_LOG_ID, "130656917", -1, errMsg,
+ strlen(errMsg));
+
+ return MTP_RESPONSE_INVALID_PARAMETER;
+ }
if (path[path.size() - 1] != '/')
path.append("/");
- path.append(name);
+ path.append(basename(name));
// check space first
if (mSendObjectFileSize > storage->getFreeSpace())
diff --git a/media/mtp/MtpServer.h b/media/mtp/MtpServer.h
index 1f8799f..8cc9a9a 100644
--- a/media/mtp/MtpServer.h
+++ b/media/mtp/MtpServer.h
@@ -34,8 +34,11 @@
class IMtpDatabase;
class MtpStorage;
+class MtpMockServer;
class MtpServer {
+ // libFuzzer testing
+ friend class MtpMockServer;
private:
IMtpDatabase* mDatabase;
diff --git a/media/mtp/MtpStringBuffer.cpp b/media/mtp/MtpStringBuffer.cpp
index cd379bf..d8d425b 100644
--- a/media/mtp/MtpStringBuffer.cpp
+++ b/media/mtp/MtpStringBuffer.cpp
@@ -26,14 +26,31 @@
namespace {
-std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> gConvert;
+const char * utf16_cerror = "__CONVERSION_ERROR__";
+const char16_t * utf8_cerror = u"__CONVERSION_ERROR__";
+
+std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> gConvert(utf16_cerror, utf8_cerror);
static std::string utf16ToUtf8(std::u16string input_str) {
- return gConvert.to_bytes(input_str);
+ std::string conversion = gConvert.to_bytes(input_str);
+
+ if (conversion == utf16_cerror) {
+ ALOGE("Unable to convert UTF-16 string to UTF-8");
+ return "";
+ } else {
+ return conversion;
+ }
}
static std::u16string utf8ToUtf16(std::string input_str) {
- return gConvert.from_bytes(input_str);
+ std::u16string conversion = gConvert.from_bytes(input_str);
+
+ if (conversion == utf8_cerror) {
+ ALOGE("Unable to convert UTF-8 string to UTF-16");
+ return u"";
+ } else {
+ return conversion;
+ }
}
} // namespace
diff --git a/media/mtp/MtpUtils.cpp b/media/mtp/MtpUtils.cpp
index 8564576..84a20d3 100644
--- a/media/mtp/MtpUtils.cpp
+++ b/media/mtp/MtpUtils.cpp
@@ -150,6 +150,7 @@
ret += copyFile(oldFile.c_str(), newFile.c_str());
}
}
+ closedir(dir);
return ret;
}
diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp
index aac6d71..7b285a6 100644
--- a/media/ndk/Android.bp
+++ b/media/ndk/Android.bp
@@ -42,6 +42,7 @@
name: "libmediandk",
srcs: [
+ "NdkJavaVMHelper.cpp",
"NdkMediaCodec.cpp",
"NdkMediaCrypto.cpp",
"NdkMediaDataSource.cpp",
@@ -78,7 +79,6 @@
"android.hidl.token@1.0-utils",
"libandroid_runtime_lazy",
"libbase",
- "libbinder",
"libdatasource",
"libmedia",
"libmediadrm",
@@ -90,11 +90,11 @@
"libutils",
"libcutils",
"libnativewindow",
- "libbinder",
"libhidlbase",
"libgui",
"libui",
"libmediandk_utils",
+ "libnativehelper",
],
export_include_dirs: ["include"],
@@ -179,6 +179,10 @@
"libcutils",
"android.hardware.graphics.bufferqueue@1.0",
],
+ header_libs: [
+ "libstagefright_foundation_headers",
+ ],
+
cflags: [
"-D__ANDROID_VNDK__",
],
diff --git a/media/ndk/NdkJavaVMHelper.cpp b/media/ndk/NdkJavaVMHelper.cpp
new file mode 100644
index 0000000..baf2744
--- /dev/null
+++ b/media/ndk/NdkJavaVMHelper.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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 "NdkJavaVMHelper"
+
+#include <media/NdkJavaVMHelper.h>
+#include <utils/Log.h>
+
+namespace android {
+
+// static
+JNIEnv *NdkJavaVMHelper::getJNIEnv() {
+ JNIEnv *env;
+ jsize nVMs;
+ JavaVM *vm;
+
+ int status = JNI_GetCreatedJavaVMs(&vm, 1, &nVMs);
+ if (status != JNI_OK || nVMs == 0 || vm == NULL) {
+ ALOGE("Failed to get JVM instance");
+ return NULL;
+ } else if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ ALOGE("Failed to get JNIEnv for JavaVM: %p", vm);
+ return NULL;
+ }
+
+ return env;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/media/ndk/NdkMediaCrypto.cpp b/media/ndk/NdkMediaCrypto.cpp
index 792fc00..741e58b 100644
--- a/media/ndk/NdkMediaCrypto.cpp
+++ b/media/ndk/NdkMediaCrypto.cpp
@@ -26,9 +26,8 @@
#include <cutils/properties.h>
#include <utils/Log.h>
#include <utils/StrongPointer.h>
-#include <binder/IServiceManager.h>
+#include <mediadrm/DrmUtils.h>
#include <mediadrm/ICrypto.h>
-#include <mediadrm/IMediaDrmService.h>
#include <android_util_Binder.h>
#include <jni.h>
@@ -36,19 +35,7 @@
using namespace android;
static sp<ICrypto> makeCrypto() {
- sp<IServiceManager> sm = defaultServiceManager();
- sp<IBinder> binder = sm->getService(String16("media.drm"));
-
- sp<IMediaDrmService> service = interface_cast<IMediaDrmService>(binder);
- if (service == NULL) {
- return NULL;
- }
-
- sp<ICrypto> crypto = service->makeCrypto();
- if (crypto == NULL || (crypto->initCheck() != OK && crypto->initCheck() != NO_INIT)) {
- return NULL;
- }
- return crypto;
+ return DrmUtils::MakeCrypto();
}
struct AMediaCrypto {
diff --git a/media/ndk/NdkMediaDataSource.cpp b/media/ndk/NdkMediaDataSource.cpp
index c1d4686..98ccd6c 100644
--- a/media/ndk/NdkMediaDataSource.cpp
+++ b/media/ndk/NdkMediaDataSource.cpp
@@ -30,6 +30,7 @@
#include <datasource/HTTPBase.h>
#include <datasource/NuCachedSource2.h>
#include <media/IMediaHTTPService.h>
+#include <media/NdkJavaVMHelper.h>
#include <media/NdkMediaError.h>
#include <media/NdkMediaDataSource.h>
#include <media/stagefright/InterfaceUtils.h>
@@ -167,7 +168,8 @@
JNIEnv *env;
const char *clazz, *method, *signature;
- env = AndroidRuntime::getJNIEnv();
+ env = NdkJavaVMHelper::getJNIEnv();
+
clazz = "android/media/MediaHTTPService";
method = "createHttpServiceBinderIfNecessary";
signature = "(Ljava/lang/String;)Landroid/os/IBinder;";
diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp
index 842216c..3af9771 100644
--- a/media/ndk/NdkMediaDrm.cpp
+++ b/media/ndk/NdkMediaDrm.cpp
@@ -20,6 +20,10 @@
#include <inttypes.h>
#include <unistd.h>
+#include <iostream>
+#include <fstream>
+#include <string>
+
#include <media/NdkMediaDrm.h>
#include <cutils/properties.h>
@@ -28,20 +32,18 @@
#include <gui/Surface.h>
#include <android-base/properties.h>
-#include <binder/PermissionController.h>
+#include <mediadrm/DrmUtils.h>
#include <mediadrm/IDrm.h>
#include <mediadrm/IDrmClient.h>
#include <media/stagefright/MediaErrors.h>
-#include <binder/IServiceManager.h>
#include <media/NdkMediaCrypto.h>
-#include <mediadrm/IMediaDrmService.h>
using namespace android;
typedef Vector<uint8_t> idvec_t;
-struct DrmListener: virtual public BnDrmClient
+struct DrmListener: virtual public IDrmClient
{
private:
AMediaDrm *mObj;
@@ -71,12 +73,27 @@
mKeysChangeListener = listener;
}
- void notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj);
+ void sendEvent(
+ DrmPlugin::EventType eventType,
+ const hardware::hidl_vec<uint8_t> &sessionId,
+ const hardware::hidl_vec<uint8_t> &data) override;
+
+ void sendExpirationUpdate(
+ const hardware::hidl_vec<uint8_t> &sessionId,
+ int64_t expiryTimeInMS) override;
+
+ void sendKeysChange(
+ const hardware::hidl_vec<uint8_t> &sessionId,
+ const std::vector<DrmKeyStatus> &keyStatusList,
+ bool hasNewUsableKey) override;
+
+ void sendSessionLostState(
+ const hardware::hidl_vec<uint8_t> &) override {}
+
};
struct AMediaDrm {
sp<IDrm> mDrm;
- sp<IDrmClient> mDrmClient;
List<idvec_t> mIds;
KeyedVector<String8, String8> mQueryResults;
Vector<uint8_t> mKeyRequest;
@@ -88,71 +105,52 @@
sp<DrmListener> mListener;
};
-void DrmListener::notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj) {
- if (!mEventListener || !mExpirationUpdateListener || !mKeysChangeListener) {
- ALOGE("No listeners are specified");
+void DrmListener::sendExpirationUpdate(
+ const hardware::hidl_vec<uint8_t> &sessionId,
+ int64_t expiryTimeInMS) {
+ if (!mExpirationUpdateListener) {
+ ALOGE("No ExpirationUpdateListener specified");
return;
}
- obj->setDataPosition(0);
+ if (expiryTimeInMS >= 0) {
+ AMediaDrmSessionId asid = {sessionId.data(), sessionId.size()};
+ (*mExpirationUpdateListener)(mObj, &asid, expiryTimeInMS);
+ } else {
+ ALOGE("expiry time negative, status=%" PRId64 "", expiryTimeInMS);
+ }
+}
- AMediaDrmSessionId sessionId = {NULL, 0};
- int32_t sessionIdSize = obj->readInt32();
- if (sessionIdSize <= 0) {
- ALOGE("Invalid session id size");
+void DrmListener::sendKeysChange(
+ const hardware::hidl_vec<uint8_t> &sessionId,
+ const std::vector<DrmKeyStatus> &keyStatusList,
+ bool hasNewUsableKey) {
+ if (!mKeysChangeListener) {
+ ALOGE("No KeysChangeListener specified");
return;
}
- std::unique_ptr<uint8_t[]> sessionIdData(new uint8_t[sessionIdSize]);
- sessionId.ptr = sessionIdData.get();
- sessionId.length = sessionIdSize;
- status_t err = obj->read(sessionIdData.get(), sessionId.length);
- if (err != OK) {
- ALOGE("Failed to read session id, error=%d", err);
- return;
- }
-
- if (DrmPlugin::kDrmPluginEventExpirationUpdate == eventType) {
- int64_t expiryTimeInMS = obj->readInt64();
- if (expiryTimeInMS >= 0) {
- (*mExpirationUpdateListener)(mObj, &sessionId, expiryTimeInMS);
- } else {
- ALOGE("Failed to read expiry time, status=%" PRId64 "", expiryTimeInMS);
- }
- return;
- } else if (DrmPlugin::kDrmPluginEventKeysChange == eventType) {
- int32_t numKeys = 0;
- err = obj->readInt32(&numKeys);
- if (err != OK) {
- ALOGE("Failed to read number of keys status, error=%d", err);
- return;
- }
-
- Vector<AMediaDrmKeyStatus> keysStatus;
- std::vector<std::unique_ptr<uint8_t[]> > dataPointers;
+ Vector<AMediaDrmKeyStatus> keysStatus;
+ for (const auto &drmKeyStatus : keyStatusList) {
AMediaDrmKeyStatus keyStatus;
+ keyStatus.keyId.ptr = drmKeyStatus.keyId.data();
+ keyStatus.keyId.length = drmKeyStatus.keyId.size();
+ keyStatus.keyType = static_cast<AMediaDrmKeyStatusType>(drmKeyStatus.type);
+ keysStatus.push(keyStatus);
+ }
- for (size_t i = 0; i < numKeys; ++i) {
- keyStatus.keyId.ptr = nullptr;
- keyStatus.keyId.length = 0;
- int32_t idSize = obj->readInt32();
- if (idSize > 0) {
- std::unique_ptr<uint8_t[]> data(new uint8_t[idSize]);
- err = obj->read(data.get(), idSize);
- if (err != OK) {
- ALOGE("Failed to read key data, error=%d", err);
- return;
- }
- keyStatus.keyId.ptr = data.get();
- keyStatus.keyId.length = idSize;
- dataPointers.push_back(std::move(data));
- }
- keyStatus.keyType = static_cast<AMediaDrmKeyStatusType>(obj->readInt32());
- keysStatus.push(keyStatus);
- }
+ AMediaDrmSessionId asid = {sessionId.data(), sessionId.size()};
+ int32_t numKeys = keyStatusList.size();
+ (*mKeysChangeListener)(mObj, &asid, keysStatus.array(), numKeys, hasNewUsableKey);
+ return;
+}
- bool hasNewUsableKey = obj->readInt32();
- (*mKeysChangeListener)(mObj, &sessionId, keysStatus.array(), numKeys, hasNewUsableKey);
+void DrmListener::sendEvent(
+ DrmPlugin::EventType eventType,
+ const hardware::hidl_vec<uint8_t> &sessionId,
+ const hardware::hidl_vec<uint8_t> &data) {
+ if (!mEventListener) {
+ ALOGE("No EventListener specified");
return;
}
@@ -176,23 +174,17 @@
ndkEventType = EVENT_SESSION_RECLAIMED;
break;
default:
- ALOGE("Invalid event DrmPlugin::EventType %d, ignored", (int)eventType);
+ ALOGE("Invalid event DrmPlugin::EventType %d, ignored", eventType);
return;
}
- int32_t dataSize = obj->readInt32();
- uint8_t *data = NULL;
+ AMediaDrmSessionId asid = {sessionId.data(), sessionId.size()};
+ int32_t dataSize = data.size();
+ const uint8_t *dataPtr = data.data();
if (dataSize > 0) {
- data = new uint8_t[dataSize];
- err = obj->read(data, dataSize);
- if (err == OK) {
- (*mEventListener)(mObj, &sessionId, ndkEventType, extra, data, dataSize);
- } else {
- ALOGE("Failed to read event data, error=%d", err);
- }
- delete [] data;
+ (*mEventListener)(mObj, &asid, ndkEventType, 0, dataPtr, dataSize);
} else {
- ALOGE("Error reading parcel: invalid event data size=%d", dataSize);
+ ALOGE("invalid event data size=%d", dataSize);
}
}
@@ -246,41 +238,20 @@
}
static status_t GetAppPackageName(String8 *packageName) {
- sp<IServiceManager> serviceManager = defaultServiceManager();
- sp<IBinder> binder = serviceManager->getService(String16("permission"));
-
- sp<IPermissionController> permissionContol = interface_cast<IPermissionController>(binder);
- if (permissionContol == NULL) {
- ALOGE("Failed to get permission service");
+ // todo(robertshih): use refactored/renamed libneuralnetworks_packageinfo which is stable
+ std::string appName;
+ std::ifstream cmdline("/proc/self/cmdline");
+ std::getline(cmdline, appName);
+ cmdline.close();
+ if (appName.empty()) {
return UNKNOWN_ERROR;
}
-
- Vector<String16> packages;
- permissionContol->getPackagesForUid(getuid(), packages);
-
- if (packages.isEmpty()) {
- ALOGE("Unable to get package name for current UID");
- return UNKNOWN_ERROR;
- }
-
- *packageName = String8(packages[0]);
+ *packageName = String8(appName.c_str());
return OK;
}
static sp<IDrm> CreateDrm() {
- sp<IServiceManager> sm = defaultServiceManager();
- sp<IBinder> binder = sm->getService(String16("media.drm"));
-
- sp<IMediaDrmService> service = interface_cast<IMediaDrmService>(binder);
- if (service == NULL) {
- return NULL;
- }
-
- sp<IDrm> drm = service->makeDrm();
- if (drm == NULL || (drm->initCheck() != OK && drm->initCheck() != NO_INIT)) {
- return NULL;
- }
- return drm;
+ return DrmUtils::MakeDrm();
}
diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp
index 51138c8..ab0cb63 100644
--- a/media/ndk/NdkMediaFormat.cpp
+++ b/media/ndk/NdkMediaFormat.cpp
@@ -341,6 +341,7 @@
EXPORT const char* AMEDIAFORMAT_KEY_LEVEL = "level";
EXPORT const char* AMEDIAFORMAT_KEY_LOCATION = "location";
EXPORT const char* AMEDIAFORMAT_KEY_LOOP = "loop";
+EXPORT const char* AMEDIAFORMAT_KEY_LOW_LATENCY = "low-latency";
EXPORT const char* AMEDIAFORMAT_KEY_LYRICIST = "lyricist";
EXPORT const char* AMEDIAFORMAT_KEY_MANUFACTURER = "manufacturer";
EXPORT const char* AMEDIAFORMAT_KEY_MAX_BIT_RATE = "max-bitrate";
diff --git a/media/ndk/include/media/NdkJavaVMHelper.h b/media/ndk/include/media/NdkJavaVMHelper.h
new file mode 100644
index 0000000..1c20275
--- /dev/null
+++ b/media/ndk/include/media/NdkJavaVMHelper.h
@@ -0,0 +1,31 @@
+/*
+ * 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 NDK_JAVA_VM_HELPER_H_
+
+#define NDK_JAVA_VM_HELPER_H_
+
+#include "jni.h"
+
+namespace android {
+
+struct NdkJavaVMHelper {
+ static JNIEnv *getJNIEnv();
+};
+
+} // namespace android
+
+#endif // NDK_JAVA_VM_HELPER_H_
\ No newline at end of file
diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h
index 41c2378..ed77aac 100644
--- a/media/ndk/include/media/NdkMediaFormat.h
+++ b/media/ndk/include/media/NdkMediaFormat.h
@@ -294,6 +294,13 @@
#endif /* __ANDROID_API__ >= 29 */
+#if __ANDROID_API__ >= 30
+/**
+ * Available since API level 30.
+ */
+extern const char* AMEDIAFORMAT_KEY_LOW_LATENCY __INTRODUCED_IN(30);
+#endif /* __ANDROID_API__ >= 30 */
+
__END_DECLS
#endif // _NDK_MEDIA_FORMAT_H
diff --git a/media/ndk/include/media/NdkMediaMuxer.h b/media/ndk/include/media/NdkMediaMuxer.h
index 3fdeea4..9de3fbf 100644
--- a/media/ndk/include/media/NdkMediaMuxer.h
+++ b/media/ndk/include/media/NdkMediaMuxer.h
@@ -51,6 +51,7 @@
typedef enum {
AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4 = 0,
AMEDIAMUXER_OUTPUT_FORMAT_WEBM = 1,
+ AMEDIAMUXER_OUTPUT_FORMAT_THREE_GPP = 2,
} OutputFormat;
#if __ANDROID_API__ >= 21
diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt
index 7531578..29f1da8 100644
--- a/media/ndk/libmediandk.map.txt
+++ b/media/ndk/libmediandk.map.txt
@@ -105,6 +105,7 @@
AMEDIAFORMAT_KEY_LEVEL; # var introduced=28
AMEDIAFORMAT_KEY_LOCATION; # var introduced=29
AMEDIAFORMAT_KEY_LOOP; # var introduced=29
+ AMEDIAFORMAT_KEY_LOW_LATENCY; # var introduced=30
AMEDIAFORMAT_KEY_LYRICIST; # var introduced=29
AMEDIAFORMAT_KEY_MANUFACTURER; # var introduced=29
AMEDIAFORMAT_KEY_MAX_BIT_RATE; # var introduced=29
diff --git a/media/tests/benchmark/src/native/decoder/C2Decoder.cpp b/media/tests/benchmark/src/native/decoder/C2Decoder.cpp
index e88d011..20a1468 100644
--- a/media/tests/benchmark/src/native/decoder/C2Decoder.cpp
+++ b/media/tests/benchmark/src/native/decoder/C2Decoder.cpp
@@ -16,8 +16,10 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "C2Decoder"
+#include <log/log.h>
#include "C2Decoder.h"
+#include <iostream>
int32_t C2Decoder::createCodec2Component(string compName, AMediaFormat *format) {
ALOGV("In %s", __func__);
@@ -87,7 +89,7 @@
work.swap(mWorkQueue.front());
mWorkQueue.pop_front();
} else {
- cout << "Wait for generating C2Work exceeded timeout" << endl;
+ std::cout << "Wait for generating C2Work exceeded timeout" << std::endl;
return -1;
}
}
@@ -110,13 +112,13 @@
status = mLinearPool->fetchLinearBlock(
alignedSize, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block);
if (status != C2_OK || block == nullptr) {
- cout << "C2LinearBlock::map() failed : " << status << endl;
+ std::cout << "C2LinearBlock::map() failed : " << status << std::endl;
return status;
}
C2WriteView view = block->map().get();
if (view.error() != C2_OK) {
- cout << "C2LinearBlock::map() failed : " << view.error() << endl;
+ std::cout << "C2LinearBlock::map() failed : " << view.error() << std::endl;
return view.error();
}
memcpy(view.base(), inputBuffer + mOffset, size);
diff --git a/media/tests/benchmark/src/native/decoder/C2Decoder.h b/media/tests/benchmark/src/native/decoder/C2Decoder.h
index 0e79d51..4a3eb96 100644
--- a/media/tests/benchmark/src/native/decoder/C2Decoder.h
+++ b/media/tests/benchmark/src/native/decoder/C2Decoder.h
@@ -17,10 +17,6 @@
#ifndef __C2_DECODER_H__
#define __C2_DECODER_H__
-#include <stdio.h>
-#include <algorithm>
-#include <fstream>
-
#include "BenchmarkC2Common.h"
#define ALIGN(_sz, _align) (((_sz) + ((_align) - 1)) & ~((_align) - 1))
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/ISchedulingPolicyService.cpp b/media/utils/ISchedulingPolicyService.cpp
index b210404..e60e230 100644
--- a/media/utils/ISchedulingPolicyService.cpp
+++ b/media/utils/ISchedulingPolicyService.cpp
@@ -62,12 +62,12 @@
return reply.readInt32();
}
- virtual int requestCpusetBoost(bool enable, const sp<IInterface>& client)
+ virtual int requestCpusetBoost(bool enable, const sp<IBinder>& client)
{
Parcel data, reply;
data.writeInterfaceToken(ISchedulingPolicyService::getInterfaceDescriptor());
data.writeInt32(enable);
- data.writeStrongBinder(IInterface::asBinder(client));
+ data.writeStrongBinder(client);
status_t status = remote()->transact(REQUEST_CPUSET_BOOST, data, &reply, 0);
if (status != NO_ERROR) {
return status;
diff --git a/media/utils/ISchedulingPolicyService.h b/media/utils/ISchedulingPolicyService.h
index e4f7c0d..6fa100a 100644
--- a/media/utils/ISchedulingPolicyService.h
+++ b/media/utils/ISchedulingPolicyService.h
@@ -29,7 +29,7 @@
virtual int requestPriority(/*pid_t*/int32_t pid, /*pid_t*/int32_t tid,
int32_t prio, bool isForApp, bool asynchronous) = 0;
- virtual int requestCpusetBoost(bool enable, const sp<IInterface>& client) = 0;
+ virtual int requestCpusetBoost(bool enable, const sp<IBinder>& client) = 0;
};
class BnSchedulingPolicyService : public BnInterface<ISchedulingPolicyService>
diff --git a/media/utils/SchedulingPolicyService.cpp b/media/utils/SchedulingPolicyService.cpp
index 4e9792f..ad38862 100644
--- a/media/utils/SchedulingPolicyService.cpp
+++ b/media/utils/SchedulingPolicyService.cpp
@@ -59,7 +59,7 @@
return ret;
}
-int requestCpusetBoost(bool enable, const sp<IInterface> &client)
+int requestCpusetBoost(bool enable, const sp<IBinder> &client)
{
int ret;
sMutex.lock();
diff --git a/media/utils/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp
index a661470..343ec05 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
@@ -61,12 +63,12 @@
static bool checkRecordingInternal(const String16& opPackageName, pid_t pid,
uid_t uid, bool start) {
- // Okay to not track in app ops as audio server is us and if
+ // Okay to not track in app ops as audio server or media server is us and if
// device is rooted security model is considered compromised.
// system_server loses its RECORD_AUDIO permission when a secondary
// user is active, but it is a core system service so let it through.
// TODO(b/141210120): UserManager.DISALLOW_RECORD_AUDIO should not affect system user 0
- if (isAudioServerOrSystemServerOrRootUid(uid)) return true;
+ if (isAudioServerOrMediaServerOrSystemServerOrRootUid(uid)) return true;
// We specify a pid and uid here as mediaserver (aka MediaRecorder or StageFrightRecorder)
// may open a record track on behalf of a client. Note that pid may be a tid.
@@ -167,9 +169,16 @@
}
bool modifyAudioRoutingAllowed() {
+ return modifyAudioRoutingAllowed(
+ IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid());
+}
+
+bool modifyAudioRoutingAllowed(pid_t pid, uid_t uid) {
+ if (isAudioServerUid(IPCThreadState::self()->getCallingUid())) return true;
// IMPORTANT: Use PermissionCache - not a runtime permission and may not change.
- bool ok = PermissionCache::checkCallingPermission(sModifyAudioRouting);
- if (!ok) ALOGE("android.permission.MODIFY_AUDIO_ROUTING");
+ bool ok = PermissionCache::checkPermission(sModifyAudioRouting, pid, uid);
+ if (!ok) ALOGE("%s(): android.permission.MODIFY_AUDIO_ROUTING denied for uid %d",
+ __func__, uid);
return ok;
}
@@ -232,9 +241,9 @@
off_t size = lseek(heap->getHeapID(), 0, SEEK_END);
lseek(heap->getHeapID(), 0, SEEK_SET);
- if (iMemory->pointer() == NULL || size < (off_t)iMemory->size()) {
+ if (iMemory->unsecurePointer() == NULL || size < (off_t)iMemory->size()) {
ALOGE("%s check failed: pointer %p size %zu fd size %u",
- __FUNCTION__, iMemory->pointer(), iMemory->size(), (uint32_t)size);
+ __FUNCTION__, iMemory->unsecurePointer(), iMemory->size(), (uint32_t)size);
return BAD_VALUE;
}
@@ -322,4 +331,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/SchedulingPolicyService.h b/media/utils/include/mediautils/SchedulingPolicyService.h
index a33539f..546cec5 100644
--- a/media/utils/include/mediautils/SchedulingPolicyService.h
+++ b/media/utils/include/mediautils/SchedulingPolicyService.h
@@ -21,7 +21,7 @@
namespace android {
-class IInterface;
+class IBinder;
// Request elevated priority for thread tid, whose thread group leader must be pid.
// The priority parameter is currently restricted to either 1 or 2.
// The asynchronous parameter should be 'true' to return immediately,
@@ -35,7 +35,7 @@
// for the server to receive death notifications. When 'enable' is 'false', server
// will attempt to move media.codec process back to the original cpuset, and
// 'client' is ignored in this case.
-int requestCpusetBoost(bool enable, const sp<IInterface> &client);
+int requestCpusetBoost(bool enable, const sp<IBinder> &client);
} // namespace android
diff --git a/media/utils/include/mediautils/ServiceUtilities.h b/media/utils/include/mediautils/ServiceUtilities.h
index f5768bd..4925cdb 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 {
@@ -58,10 +59,11 @@
return multiuser_get_app_id(uid) == AID_SYSTEM || uid == AID_AUDIOSERVER;
}
-// used for calls that should come from system_server or audio_server and
+// used for calls that should come from system_server or audio_server or media server and
// include AID_ROOT for command-line tests.
-static inline bool isAudioServerOrSystemServerOrRootUid(uid_t uid) {
- return multiuser_get_app_id(uid) == AID_SYSTEM || uid == AID_AUDIOSERVER || uid == AID_ROOT;
+static inline bool isAudioServerOrMediaServerOrSystemServerOrRootUid(uid_t uid) {
+ return multiuser_get_app_id(uid) == AID_SYSTEM || uid == AID_AUDIOSERVER
+ || uid == AID_MEDIA || uid == AID_ROOT;
}
// Mediaserver may forward the client PID and UID as part of a binder interface call;
@@ -84,6 +86,7 @@
bool captureHotwordAllowed(const String16& opPackageName, pid_t pid, uid_t uid);
bool settingsAllowed();
bool modifyAudioRoutingAllowed();
+bool modifyAudioRoutingAllowed(pid_t pid, uid_t uid);
bool modifyDefaultAudioEffectsAllowed();
bool modifyDefaultAudioEffectsAllowed(pid_t pid, uid_t uid);
bool dumpAllowed();
@@ -116,6 +119,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..3873600 100644
--- a/services/audioflinger/Android.bp
+++ b/services/audioflinger/Android.bp
@@ -9,6 +9,7 @@
"AudioStreamOut.cpp",
"AudioWatchdog.cpp",
"BufLog.cpp",
+ "DeviceEffectManager.cpp",
"Effects.cpp",
"FastCapture.cpp",
"FastCaptureDumpState.cpp",
@@ -62,6 +63,7 @@
],
header_libs: [
+ "libaudiohal_headers",
"libmedia_headers",
],
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index d0d1c32..ee4c5ba 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -170,6 +170,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
@@ -347,6 +348,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 +386,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 +444,30 @@
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\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);
+ result.appendFormat(" %7d %4d %7d\n", r->mSessionid, r->mCnt, r->mPid);
}
write(fd, result.string(), result.size());
}
@@ -558,6 +579,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);
@@ -664,7 +687,7 @@
return new NBLog::Writer();
}
success:
- NBLog::Shared *sharedRawPtr = (NBLog::Shared *) shared->pointer();
+ NBLog::Shared *sharedRawPtr = (NBLog::Shared *) shared->unsecurePointer();
new((void *) sharedRawPtr) NBLog::Shared(); // placement new here, but the corresponding
// explicit destructor not needed since it is POD
sMediaLogService->registerWriter(shared, size, name);
@@ -1171,16 +1194,16 @@
return mute;
}
-void AudioFlinger::setRecordSilenced(uid_t uid, bool silenced)
+void AudioFlinger::setRecordSilenced(audio_port_handle_t portId, bool silenced)
{
- ALOGV("AudioFlinger::setRecordSilenced(uid:%d, silenced:%d)", uid, silenced);
+ ALOGV("AudioFlinger::setRecordSilenced(portId:%d, silenced:%d)", portId, silenced);
AutoMutex lock(mLock);
for (size_t i = 0; i < mRecordThreads.size(); i++) {
- mRecordThreads[i]->setRecordSilenced(uid, silenced);
+ mRecordThreads[i]->setRecordSilenced(portId, silenced);
}
for (size_t i = 0; i < mMmapThreads.size(); i++) {
- mMmapThreads[i]->setRecordSilenced(uid, silenced);
+ mMmapThreads[i]->setRecordSilenced(portId, silenced);
}
}
@@ -1582,40 +1605,64 @@
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();
- size_t frames = 0;
- for (;;) {
- // Note: config is currently a const parameter for get_input_buffer_size()
- // but we use a copy from proposed in case config changes from the call.
- config = proposed;
- status_t result = dev->getInputBufferSize(&config, &frames);
- if (result == OK && frames != 0) {
- break; // hal success, config is the result
- }
- // change one parameter of the configuration each iteration to a more "common" value
- // to see if the device will support it.
- if (proposed.format != AUDIO_FORMAT_PCM_16_BIT) {
- proposed.format = AUDIO_FORMAT_PCM_16_BIT;
- } else if (proposed.sample_rate != 44100) { // 44.1 is claimed as must in CDD as well as
- proposed.sample_rate = 44100; // legacy AudioRecord.java. TODO: Query hw?
- } else {
- ALOGW("getInputBufferSize failed with minimum buffer size sampleRate %u, "
- "format %#x, channelMask 0x%X",
- sampleRate, format, channelMask);
- break; // retries failed, break out of loop with frames == 0.
- }
- }
+ std::vector<audio_channel_mask_t> channelMasks = {channelMask};
+ if (channelMask != AUDIO_CHANNEL_IN_MONO)
+ channelMasks.push_back(AUDIO_CHANNEL_IN_MONO);
+ if (channelMask != AUDIO_CHANNEL_IN_STEREO)
+ channelMasks.push_back(AUDIO_CHANNEL_IN_STEREO);
+
+ std::vector<audio_format_t> formats = {format};
+ if (format != AUDIO_FORMAT_PCM_16_BIT)
+ formats.push_back(AUDIO_FORMAT_PCM_16_BIT);
+
+ std::vector<uint32_t> sampleRates = {sampleRate};
+ static const uint32_t SR_44100 = 44100;
+ static const uint32_t SR_48000 = 48000;
+
+ if (sampleRate != SR_48000)
+ sampleRates.push_back(SR_48000);
+ if (sampleRate != SR_44100)
+ sampleRates.push_back(SR_44100);
+
mHardwareStatus = AUDIO_HW_IDLE;
- if (frames > 0 && config.sample_rate != sampleRate) {
- frames = destinationFramesPossible(frames, sampleRate, config.sample_rate);
+
+ // 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) {
+ continue;
+ }
+
+ if (config.sample_rate != sampleRate || config.channel_mask != channelMask ||
+ config.format != format) {
+ uint32_t dstChannelCount = audio_channel_count_from_in_mask(channelMask);
+ uint32_t srcChannelCount =
+ audio_channel_count_from_in_mask(config.channel_mask);
+ size_t srcFrames =
+ bytes / audio_bytes_per_frame(srcChannelCount, config.format);
+ size_t dstFrames = destinationFramesPossible(
+ srcFrames, config.sample_rate, sampleRate);
+ bytes = dstFrames * audio_bytes_per_frame(dstChannelCount, format);
+ }
+ return bytes;
+ }
+ }
}
- return frames; // may be converted to bytes at the Java level.
+
+ ALOGW("getInputBufferSize failed with minimum buffer size sampleRate %u, "
+ "format %#x, channelMask %#x",sampleRate, format, channelMask);
+ return 0;
}
uint32_t AudioFlinger::getInputFramesLost(audio_io_handle_t ioHandle) const
@@ -1670,13 +1717,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);
@@ -1816,8 +1866,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)
{
}
@@ -2649,9 +2700,6 @@
return 0;
}
- // Some flags are specific to framework and must not leak to the HAL.
- flags = static_cast<audio_input_flags_t>(flags & ~AUDIO_INPUT_FRAMEWORK_FLAGS);
-
// Audio Policy can request a specific handle for hardware hotword.
// The goal here is not to re-open an already opened input.
// It is to use a pre-assigned I/O handle.
@@ -3294,7 +3342,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,
@@ -3358,7 +3406,6 @@
lStatus = BAD_VALUE;
goto Exit;
}
- //TODO: add check on device ID when added to arguments
} else {
// general sessionId.
@@ -3411,6 +3458,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
@@ -3505,6 +3569,7 @@
}
}
+Register:
if (lStatus == NO_ERROR || lStatus == ALREADY_EXISTS) {
// Check CPU and memory usage
sp<EffectBase> effect = handle->effect().promote();
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index e49229e..5c975e1 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -70,11 +70,12 @@
#include <media/AudioMixer.h>
#include <media/DeviceDescriptorBase.h>
#include <media/ExtendedAudioBufferProvider.h>
-#include <media/LinearMap.h>
#include <media/VolumeShaper.h>
+#include <mediautils/ServiceUtilities.h>
#include <audio_utils/clock.h>
#include <audio_utils/FdToString.h>
+#include <audio_utils/LinearMap.h>
#include <audio_utils/SimpleLog.h>
#include <audio_utils/TimestampVerifier.h>
@@ -165,7 +166,7 @@
virtual status_t setMicMute(bool state);
virtual bool getMicMute() const;
- virtual void setRecordSilenced(uid_t uid, bool silenced);
+ virtual void setRecordSilenced(audio_port_handle_t portId, bool silenced);
virtual status_t setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs);
virtual String8 getParameters(audio_io_handle_t ioHandle, const String8& keys) const;
@@ -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;
@@ -474,10 +481,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 +497,7 @@
const sp<AudioFlinger> mAudioFlinger;
const pid_t mPid;
+ const uid_t mUid;
const sp<IAudioFlingerClient> mAudioFlingerClient;
};
@@ -537,6 +548,10 @@
class EffectModule;
class EffectHandle;
class EffectChain;
+ class DeviceEffectProxy;
+ class DeviceEffectManager;
+ class PatchPanel;
+ class DeviceEffectManagerCallback;
struct AudioStreamIn;
struct TeePatch;
@@ -574,9 +589,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.
@@ -922,8 +939,12 @@
PatchPanel mPatchPanel;
sp<EffectsFactoryHalInterface> mEffectsFactoryHal;
+ DeviceEffectManager mDeviceEffectManager;
+
bool mSystemReady;
+ mediautils::UidInfo mUidInfo;
+
SimpleLog mRejectedSetParameterLog;
SimpleLog mAppSetParameterLog;
SimpleLog mSystemSetParameterLog;
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 7f34c14..70c56e3 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -512,7 +512,8 @@
effect_descriptor_t *desc,
int id,
audio_session_t sessionId,
- bool pinned)
+ 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()
@@ -531,7 +532,7 @@
// create effect engine from effect factory
mStatus = callback->createEffectHal(
- &desc->uuid, sessionId, AUDIO_PORT_HANDLE_NONE, &mEffectInterface);
+ &desc->uuid, sessionId, deviceId, &mEffectInterface);
if (mStatus != NO_ERROR) {
return;
}
@@ -1602,7 +1603,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;
@@ -1610,7 +1611,7 @@
int bufOffset = ((sizeof(effect_param_cblk_t) - 1) / sizeof(int) + 1) * sizeof(int);
mCblkMemory = client->heap()->allocate(EFFECT_PARAM_BUFFER_SIZE + bufOffset);
if (mCblkMemory == 0 ||
- (mCblk = static_cast<effect_param_cblk_t *>(mCblkMemory->pointer())) == NULL) {
+ (mCblk = static_cast<effect_param_cblk_t *>(mCblkMemory->unsecurePointer())) == NULL) {
ALOGE("not enough memory for Effect size=%zu", EFFECT_PARAM_BUFFER_SIZE +
sizeof(effect_param_cblk_t));
mCblkMemory.clear();
@@ -1790,12 +1791,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;
@@ -2085,7 +2087,7 @@
bool pinned)
{
Mutex::Autolock _l(mLock);
- effect = new EffectModule(mEffectCallback, 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);
@@ -2919,4 +2921,326 @@
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 ea51c2c..40bb226 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -33,7 +33,7 @@
virtual bool isOffload() const = 0;
virtual bool isOffloadOrDirect() const = 0;
virtual bool isOffloadOrMmap() const = 0;
- virtual uint32_t sampleRate() 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;
@@ -159,6 +159,7 @@
status_t updatePolicyState();
virtual sp<EffectModule> asEffectModule() { return nullptr; }
+ virtual sp<DeviceEffectProxy> asDeviceEffectProxy() { return nullptr; }
void dump(int fd, const Vector<String16>& args);
@@ -206,7 +207,8 @@
effect_descriptor_t *desc,
int id,
audio_session_t sessionId,
- bool pinned);
+ bool pinned,
+ audio_port_handle_t deviceId);
virtual ~EffectModule();
void process();
@@ -613,3 +615,96 @@
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(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:
+ ProxyCallback(DeviceEffectProxy *proxy,
+ const sp<DeviceEffectManagerCallback>& callback)
+ : mProxy(proxy), 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 ee5bd75..5501856 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -168,9 +168,9 @@
if (hwDevice != 0) {
hwDevice->releaseAudioPatch(removedPatch.mHalHandle);
}
+ halHandle = removedPatch.mHalHandle;
}
- mPatches.erase(iter);
- removeSoftwarePatchFromInsertedModules(*handle);
+ erasePatch(*handle);
}
}
@@ -325,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;
}
}
@@ -387,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;
}
}
@@ -405,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);
}
@@ -633,8 +640,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;
@@ -710,11 +730,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)
@@ -798,16 +823,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..4e0d243 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; }
@@ -150,14 +155,36 @@
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 +192,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 +212,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/RecordTracks.h b/services/audioflinger/RecordTracks.h
index d5257bd..d87239d 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -25,7 +25,8 @@
~OpRecordAudioMonitor() override;
bool hasOpRecordAudio() const;
- static sp<OpRecordAudioMonitor> createIfNeeded(uid_t uid, const String16& opPackageName);
+ static sp<OpRecordAudioMonitor> createIfNeeded
+ (uid_t uid, const audio_attributes_t& attr, const String16& opPackageName);
private:
OpRecordAudioMonitor(uid_t uid, const String16& opPackageName);
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 14364b7..87b72fb 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1423,7 +1423,10 @@
if (effectBase == nullptr) {
return;
}
- effect = static_cast<EffectModule *>(effectBase.get());
+ effect = effectBase->asEffectModule();
+ if (effect == nullptr) {
+ return;
+ }
// restore suspended effects if the disconnected handle was enabled and the last one.
remove = (effect->removeHandle(handle) == 0) && (!effect->isPinned() || unpinIfLast);
if (remove) {
@@ -1742,7 +1745,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.
@@ -2132,9 +2135,9 @@
// More than 2 channels does not require stronger alignment than stereo
alignment <<= 1;
}
- if (((uintptr_t)sharedBuffer->pointer() & (alignment - 1)) != 0) {
+ if (((uintptr_t)sharedBuffer->unsecurePointer() & (alignment - 1)) != 0) {
ALOGE("Invalid buffer alignment: address %p, channel count %u",
- sharedBuffer->pointer(), channelCount);
+ sharedBuffer->unsecurePointer(), channelCount);
lStatus = BAD_VALUE;
goto Exit;
}
@@ -2662,7 +2665,7 @@
LOG_ALWAYS_FATAL_IF(result != OK,
"Error when retrieving output stream buffer size: %d", result);
mFrameCount = mBufferSize / mFrameSize;
- if (mFrameCount & 15) {
+ if ((mType == MIXER || mType == DUPLICATING) && (mFrameCount & 15)) {
ALOGW("HAL output buffer size is %zu frames but AudioMixer requires multiples of 16 frames",
mFrameCount);
}
@@ -3646,7 +3649,13 @@
// Tally underrun frames as we are inserting 0s here.
for (const auto& track : activeTracks) {
- if (track->mFillingUpStatus == Track::FS_ACTIVE) {
+ if (track->mFillingUpStatus == Track::FS_ACTIVE
+ && !track->isStopped()
+ && !track->isPaused()
+ && !track->isTerminated()) {
+ ALOGV("%s: track(%d) %s underrun due to thread sleep of %zu frames",
+ __func__, track->id(), track->getTrackStateAsString(),
+ mNormalFrameCount);
track->mAudioTrackServerProxy->tallyUnderrunFrames(mNormalFrameCount);
}
}
@@ -6779,7 +6788,7 @@
sp<IMemory> pipeMemory;
if ((roHeap == 0) ||
(pipeMemory = roHeap->allocate(pipeSize)) == 0 ||
- (pipeBuffer = pipeMemory->pointer()) == nullptr) {
+ (pipeBuffer = pipeMemory->unsecurePointer()) == nullptr) {
ALOGE("not enough memory for pipe buffer size=%zu; "
"roHeap=%p, pipeMemory=%p, pipeBuffer=%p; roHeapSize: %lld",
pipeSize, roHeap.get(), pipeMemory.get(), pipeBuffer,
@@ -7957,12 +7966,12 @@
write(fd, result.string(), result.size());
}
-void AudioFlinger::RecordThread::setRecordSilenced(uid_t uid, bool silenced)
+void AudioFlinger::RecordThread::setRecordSilenced(audio_port_handle_t portId, bool silenced)
{
Mutex::Autolock _l(mLock);
for (size_t i = 0; i < mTracks.size() ; i++) {
sp<RecordTrack> track = mTracks[i];
- if (track != 0 && track->uid() == uid) {
+ if (track != 0 && track->portId() == portId) {
track->setSilenced(silenced);
}
}
@@ -9481,11 +9490,11 @@
mInput->stream->updateSinkMetadata(metadata);
}
-void AudioFlinger::MmapCaptureThread::setRecordSilenced(uid_t uid, bool silenced)
+void AudioFlinger::MmapCaptureThread::setRecordSilenced(audio_port_handle_t portId, bool silenced)
{
Mutex::Autolock _l(mLock);
for (size_t i = 0; i < mActiveTracks.size() ; i++) {
- if (mActiveTracks[i]->uid() == uid) {
+ if (mActiveTracks[i]->portId() == portId) {
mActiveTracks[i]->setSilenced_l(silenced);
broadcast_l();
}
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 4c53e28..f1adc23 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1645,7 +1645,7 @@
void checkBtNrec();
// Sets the UID records silence
- void setRecordSilenced(uid_t uid, bool silenced);
+ void setRecordSilenced(audio_port_handle_t portId, bool silenced);
status_t getActiveMicrophones(std::vector<media::MicrophoneInfo>* activeMicrophones);
@@ -1816,7 +1816,8 @@
virtual void invalidateTracks(audio_stream_type_t streamType __unused) {}
// Sets the UID records silence
- virtual void setRecordSilenced(uid_t uid __unused, bool silenced __unused) {}
+ virtual void setRecordSilenced(audio_port_handle_t portId __unused,
+ bool silenced __unused) {}
protected:
void dumpInternals_l(int fd, const Vector<String16>& args) override;
@@ -1906,7 +1907,8 @@
void updateMetadata_l() override;
void processVolume_l() override;
- void setRecordSilenced(uid_t uid, bool silenced) override;
+ void setRecordSilenced(audio_port_handle_t portId,
+ bool silenced) override;
virtual void toAudioPortConfig(struct audio_port_config *config);
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index 52e7d59..91dbfa4 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -202,9 +202,51 @@
audio_format_t format() const { return mFormat; }
int id() const { return mId; }
+ const char *getTrackStateAsString() const {
+ if (isTerminated()) {
+ return "TERMINATED";
+ }
+ switch (mState) {
+ case IDLE:
+ return "IDLE";
+ case STOPPING_1: // for Fast and Offload
+ return "STOPPING_1";
+ case STOPPING_2: // for Fast and Offload
+ return "STOPPING_2";
+ case STOPPED:
+ return "STOPPED";
+ case RESUMING:
+ return "RESUMING";
+ case ACTIVE:
+ return "ACTIVE";
+ case PAUSING:
+ return "PAUSING";
+ case PAUSED:
+ return "PAUSED";
+ case FLUSHED:
+ return "FLUSHED";
+ case STARTING_1: // for RecordTrack
+ return "STARTING_1";
+ case STARTING_2: // for RecordTrack
+ return "STARTING_2";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
protected:
DISALLOW_COPY_AND_ASSIGN(TrackBase);
+ void releaseCblk() {
+ if (mCblk != nullptr) {
+ mCblk->~audio_track_cblk_t(); // destroy our shared-structure.
+ if (mClient == 0) {
+ free(mCblk);
+ }
+ mCblk = nullptr;
+ }
+ }
+
// AudioBufferProvider interface
virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer) = 0;
virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer);
@@ -238,7 +280,7 @@
// Upper case characters are final states.
// Lower case characters are transitory.
- const char *getTrackStateString() const {
+ const char *getTrackStateAsCodedString() const {
if (isTerminated()) {
return "T ";
}
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index e4402bd..2437202 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -150,7 +150,7 @@
if (client != 0) {
mCblkMemory = client->heap()->allocate(size);
if (mCblkMemory == 0 ||
- (mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer())) == NULL) {
+ (mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->unsecurePointer())) == NULL) {
ALOGE("%s(%d): not enough memory for AudioTrack size=%zu", __func__, mId, size);
client->heap()->dump("AudioTrack");
mCblkMemory.clear();
@@ -172,7 +172,7 @@
const sp<MemoryDealer> roHeap(thread->readOnlyHeap());
if (roHeap == 0 ||
(mBufferMemory = roHeap->allocate(bufferSize)) == 0 ||
- (mBuffer = mBufferMemory->pointer()) == NULL) {
+ (mBuffer = mBufferMemory->unsecurePointer()) == NULL) {
ALOGE("%s(%d): not enough memory for read-only buffer size=%zu",
__func__, mId, bufferSize);
if (roHeap != 0) {
@@ -187,7 +187,7 @@
case ALLOC_PIPE:
mBufferMemory = thread->pipeMemory();
// mBuffer is the virtual address as seen from current process (mediaserver),
- // and should normally be coming from mBufferMemory->pointer().
+ // and should normally be coming from mBufferMemory->unsecurePointer().
// However in this case the TrackBase does not reference the buffer directly.
// It should references the buffer via the pipe.
// Therefore, to detect incorrect usage of the buffer, we set mBuffer to NULL.
@@ -239,12 +239,7 @@
{
// delete the proxy before deleting the shared memory it refers to, to avoid dangling reference
mServerProxy.clear();
- if (mCblk != NULL) {
- mCblk->~audio_track_cblk_t(); // destroy our shared-structure.
- if (mClient == 0) {
- free(mCblk);
- }
- }
+ releaseCblk();
mCblkMemory.clear(); // free the shared memory before releasing the heap it belongs to
if (mClient != 0) {
// Client destructor must run with AudioFlinger client mutex locked
@@ -516,7 +511,11 @@
audio_port_handle_t portId,
size_t frameCountToBeReady)
: TrackBase(thread, client, attr, sampleRate, format, channelMask, frameCount,
- (sharedBuffer != 0) ? sharedBuffer->pointer() : buffer,
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ (sharedBuffer != 0) ? sharedBuffer->unsecurePointer() : buffer,
(sharedBuffer != 0) ? sharedBuffer->size() : bufferSize,
sessionId, creatorPid, uid, true /*isOut*/,
(type == TYPE_PATCH) ? ( buffer == NULL ? ALLOC_LOCAL : ALLOC_NONE) : ALLOC_CBLK,
@@ -547,25 +546,27 @@
ALOG_ASSERT(!(client == 0 && sharedBuffer != 0));
ALOGV_IF(sharedBuffer != 0, "%s(%d): sharedBuffer: %p, size: %zu",
- __func__, mId, sharedBuffer->pointer(), sharedBuffer->size());
+ __func__, mId, sharedBuffer->unsecurePointer(), sharedBuffer->size());
if (mCblk == NULL) {
return;
}
+ if (!thread->isTrackAllowed_l(channelMask, format, sessionId, uid)) {
+ ALOGE("%s(%d): no more tracks available", __func__, mId);
+ releaseCblk(); // this makes the track invalid.
+ return;
+ }
+
if (sharedBuffer == 0) {
mAudioTrackServerProxy = new AudioTrackServerProxy(mCblk, mBuffer, frameCount,
mFrameSize, !isExternalTrack(), sampleRate);
} else {
mAudioTrackServerProxy = new StaticAudioTrackServerProxy(mCblk, mBuffer, frameCount,
- mFrameSize);
+ mFrameSize, sampleRate);
}
mServerProxy = mAudioTrackServerProxy;
- if (!thread->isTrackAllowed_l(channelMask, format, sessionId, uid)) {
- ALOGE("%s(%d): no more tracks available", __func__, mId);
- return;
- }
// only allocate a fast track index if we were able to allocate a normal track name
if (flags & AUDIO_OUTPUT_FLAG_FAST) {
// FIXME: Not calling framesReadyIsCalledByMultipleThreads() exposes a potential
@@ -742,7 +743,7 @@
(mClient == 0) ? getpid() : mClient->pid(),
mSessionId,
mPortId,
- getTrackStateString(),
+ getTrackStateAsCodedString(),
mCblk->mFlags,
mFormat,
@@ -1897,7 +1898,7 @@
// static
sp<AudioFlinger::RecordThread::OpRecordAudioMonitor>
AudioFlinger::RecordThread::OpRecordAudioMonitor::createIfNeeded(
- uid_t uid, const String16& opPackageName)
+ uid_t uid, const audio_attributes_t& attr, const String16& opPackageName)
{
if (isServiceUid(uid)) {
ALOGV("not silencing record for service uid:%d pack:%s",
@@ -1905,6 +1906,13 @@
return nullptr;
}
+ // Capturing from FM TUNER output is not controlled by OP_RECORD_AUDIO
+ // because it does not affect users privacy as does capturing from an actual microphone.
+ if (attr.source == AUDIO_SOURCE_FM_TUNER) {
+ ALOGV("not muting FM TUNER capture for uid %d", uid);
+ return nullptr;
+ }
+
if (opPackageName.size() == 0) {
Vector<String16> packages;
// no package name, happens with SL ES clients
@@ -2070,7 +2078,7 @@
mRecordBufferConverter(NULL),
mFlags(flags),
mSilenced(false),
- mOpRecordAudioMonitor(OpRecordAudioMonitor::createIfNeeded(uid, opPackageName))
+ mOpRecordAudioMonitor(OpRecordAudioMonitor::createIfNeeded(uid, attr, opPackageName))
{
if (mCblk == NULL) {
return;
@@ -2233,7 +2241,7 @@
(mClient == 0) ? getpid() : mClient->pid(),
mSessionId,
mPortId,
- getTrackStateString(),
+ getTrackStateAsCodedString(),
mCblk->mFlags,
mFormat,
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index 1fe60d4..dd0cd9b 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -69,6 +69,14 @@
API_INPUT_TELEPHONY_RX, // used for capture from telephony RX path
} input_type_t;
+ typedef enum {
+ API_OUTPUT_INVALID = -1,
+ API_OUTPUT_LEGACY = 0,// e.g. audio playing to speaker
+ API_OUT_MIX_PLAYBACK, // used for "remote submix" playback of audio from remote source
+ // to local capture
+ API_OUTPUT_TELEPHONY_TX, // used for playback to telephony TX path
+ } output_type_t;
+
public:
virtual ~AudioPolicyInterface() {}
//
@@ -115,7 +123,8 @@
audio_output_flags_t *flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId,
- std::vector<audio_io_handle_t> *secondaryOutputs) = 0;
+ std::vector<audio_io_handle_t> *secondaryOutputs,
+ output_type_t *outputType) = 0;
// indicates to the audio policy manager that the output starts being used by corresponding stream.
virtual status_t startOutput(audio_port_handle_t portId) = 0;
// indicates to the audio policy manager that the output stops being used by corresponding stream.
@@ -183,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,
@@ -260,7 +273,7 @@
virtual status_t getHwOffloadEncodingFormatsSupportedForA2DP(
std::vector<audio_format_t> *formats) = 0;
- virtual void setAppState(uid_t uid, app_state_t state) = 0;
+ virtual void setAppState(audio_port_handle_t portId, app_state_t state) = 0;
virtual status_t listAudioProductStrategies(AudioProductStrategyVector &strategies) = 0;
@@ -272,6 +285,8 @@
virtual status_t getVolumeGroupFromAudioAttributes(const AudioAttributes &aa,
volume_group_t &volumeGroup) = 0;
+ virtual bool isCallScreenModeSupported() = 0;
+
virtual status_t setPreferredDeviceForStrategy(product_strategy_t strategy,
const AudioDeviceTypeAddr &device) = 0;
@@ -392,6 +407,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/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
index ec82873..c67a006 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
@@ -110,7 +110,7 @@
RecordClientVector clientsList(bool activeOnly = false,
audio_source_t source = AUDIO_SOURCE_DEFAULT, bool preferredDeviceOnly = false) const;
- void setAppState(uid_t uid, app_state_t state);
+ void setAppState(audio_port_handle_t portId, app_state_t state);
// implementation of ClientMapHandler<RecordClientDescriptor>
void addClient(const sp<RecordClientDescriptor> &client) override;
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h
index 56596f5..e59386f 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h
@@ -45,7 +45,8 @@
mAvailableOutputDevices(availableOutputDevices),
mAvailableInputDevices(availableInputDevices),
mDefaultOutputDevice(defaultOutputDevice),
- mIsSpeakerDrcEnabled(false)
+ mIsSpeakerDrcEnabled(false),
+ mIsCallScreenModeSupported(false)
{}
const std::string& getSource() const {
@@ -95,6 +96,14 @@
mIsSpeakerDrcEnabled = isSpeakerDrcEnabled;
}
+ bool isCallScreenModeSupported() const { return mIsCallScreenModeSupported; }
+
+ void setCallScreenModeSupported(bool isCallScreenModeSupported)
+ {
+ mIsCallScreenModeSupported = isCallScreenModeSupported;
+ }
+
+
const HwModuleCollection getHwModules() const { return mHwModules; }
const DeviceVector &getAvailableInputDevices() const
@@ -189,6 +198,7 @@
// DEVICE_CATEGORY_SPEAKER path to boost soft sounds, used to adjust volume curves accordingly.
// Note: remove also speaker_drc_enabled from global configuration of XML config file.
bool mIsSpeakerDrcEnabled;
+ bool mIsCallScreenModeSupported;
SurroundFormats mSurroundFormats;
};
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
index cb3c953..b963121 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
@@ -463,13 +463,13 @@
return enabledEffects;
}
-void AudioInputDescriptor::setAppState(uid_t uid, app_state_t state)
+void AudioInputDescriptor::setAppState(audio_port_handle_t portId, app_state_t state)
{
RecordClientVector clients = clientsList(false /*activeOnly*/);
RecordClientVector updatedClients;
for (const auto& client : clients) {
- if (uid == client->uid()) {
+ if (portId == client->portId()) {
bool wasSilenced = client->isSilenced();
client->setAppState(state);
if (client->active() && wasSilenced != client->isSilenced()) {
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
index 20c0a24..b1103ab 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
@@ -197,7 +197,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);
+ }
}
}
}
diff --git a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
index 4de8d3b..4376802 100644
--- a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
@@ -199,6 +199,7 @@
struct Attributes
{
static constexpr const char *speakerDrcEnabled = "speaker_drc_enabled";
+ static constexpr const char *callScreenModeSupported= "call_screen_mode_supported";
};
static status_t deserialize(const xmlNode *root, AudioPolicyConfig *config);
@@ -680,14 +681,17 @@
{
for (const xmlNode *cur = root->xmlChildrenNode; cur != NULL; cur = cur->next) {
if (!xmlStrcmp(cur->name, reinterpret_cast<const xmlChar*>(tag))) {
- std::string speakerDrcEnabled =
- getXmlAttribute(cur, Attributes::speakerDrcEnabled);
- bool isSpeakerDrcEnabled;
- if (!speakerDrcEnabled.empty() &&
- convertTo<std::string, bool>(speakerDrcEnabled, isSpeakerDrcEnabled)) {
- config->setSpeakerDrcEnabled(isSpeakerDrcEnabled);
+ bool value;
+ std::string attr = getXmlAttribute(cur, Attributes::speakerDrcEnabled);
+ if (!attr.empty() &&
+ convertTo<std::string, bool>(attr, value)) {
+ config->setSpeakerDrcEnabled(value);
}
- return NO_ERROR;
+ attr = getXmlAttribute(cur, Attributes::callScreenModeSupported);
+ if (!attr.empty() &&
+ convertTo<std::string, bool>(attr, value)) {
+ config->setCallScreenModeSupported(value);
+ }
}
}
return NO_ERROR;
diff --git a/services/audiopolicy/config/audio_policy_volumes.xml b/services/audiopolicy/config/audio_policy_volumes.xml
index ddd031a..1dec6f4 100644
--- a/services/audiopolicy/config/audio_policy_volumes.xml
+++ b/services/audiopolicy/config/audio_policy_volumes.xml
@@ -44,7 +44,7 @@
<volume stream="AUDIO_STREAM_VOICE_CALL" deviceCategory="DEVICE_CATEGORY_EXT_MEDIA"
ref="DEFAULT_MEDIA_VOLUME_CURVE"/>
<volume stream="AUDIO_STREAM_VOICE_CALL" deviceCategory="DEVICE_CATEGORY_HEARING_AID"
- ref="DEFAULT_HEARING_AID_VOLUME_CURVE"/>
+ ref="DEFAULT_NON_MUTABLE_HEARING_AID_VOLUME_CURVE"/>
<volume stream="AUDIO_STREAM_SYSTEM" deviceCategory="DEVICE_CATEGORY_HEADSET">
<point>1,-3000</point>
<point>33,-2600</point>
diff --git a/services/audiopolicy/config/msd_audio_policy_configuration.xml b/services/audiopolicy/config/msd_audio_policy_configuration.xml
index db17bc6..305cbe6 100644
--- a/services/audiopolicy/config/msd_audio_policy_configuration.xml
+++ b/services/audiopolicy/config/msd_audio_policy_configuration.xml
@@ -40,7 +40,7 @@
channelMasks="AUDIO_CHANNEL_OUT_MONO,AUDIO_CHANNEL_OUT_STEREO,AUDIO_CHANNEL_OUT_5POINT1,AUDIO_CHANNEL_OUT_7POINT1"/>
</mixPort>
<!-- The HW AV Sync flag is not required, but is recommended -->
- <mixPort name="ms12 output" role="sink" flags="AUDIO_INPUT_FLAG_HW_AV_SYNC">
+ <mixPort name="ms12 output" role="sink" flags="AUDIO_INPUT_FLAG_HW_AV_SYNC|AUDIO_INPUT_FLAG_DIRECT">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
<profile name="" format="AUDIO_FORMAT_AC3"
diff --git a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h
index 6331856..fbce801 100644
--- a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h
+++ b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h
@@ -111,6 +111,13 @@
}
},
},
+ {"STRATEGY_CALL_ASSISTANT",
+ {
+ {"", AUDIO_STREAM_PATCH, "AUDIO_STREAM_PATCH",
+ {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_CALL_ASSISTANT, AUDIO_SOURCE_DEFAULT, 0, ""}}
+ }
+ },
+ },
{"STRATEGY_TRANSMITTED_THROUGH_SPEAKER",
{
{"", AUDIO_STREAM_TTS, "AUDIO_STREAM_TTS",
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index 02b99d0..a3fa974 100755
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -53,6 +53,7 @@
{ "STRATEGY_ACCESSIBILITY", STRATEGY_ACCESSIBILITY },
{ "STRATEGY_REROUTING", STRATEGY_REROUTING },
{ "STRATEGY_PATCH", STRATEGY_REROUTING }, // boiler to manage stream patch volume
+ { "STRATEGY_CALL_ASSISTANT", STRATEGY_CALL_ASSISTANT },
};
Engine::Engine()
@@ -197,6 +198,7 @@
audio_devices_t txDevice = getDeviceForInputSource(
AUDIO_SOURCE_VOICE_COMMUNICATION)->type();
sp<AudioOutputDescriptor> primaryOutput = outputs.getPrimaryOutput();
+ LOG_ALWAYS_FATAL_IF(primaryOutput == nullptr, "Primary output not found");
DeviceVector availPrimaryInputDevices =
availableInputDevices.getDevicesFromHwModule(primaryOutput->getModuleHandle());
@@ -442,6 +444,10 @@
}
} break;
+ case STRATEGY_CALL_ASSISTANT:
+ devices = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_TELEPHONY_TX);
+ break;
+
default:
ALOGW("getDevicesForStrategy() unknown strategy: %d", strategy);
break;
@@ -470,8 +476,8 @@
const SwAudioOutputCollection &outputs = getApmObserver()->getOutputs();
DeviceVector availableDevices = availableInputDevices;
sp<AudioOutputDescriptor> primaryOutput = outputs.getPrimaryOutput();
- DeviceVector availablePrimaryDevices = availableInputDevices.getDevicesFromHwModule(
- primaryOutput->getModuleHandle());
+ DeviceVector availablePrimaryDevices = primaryOutput == nullptr ? DeviceVector()
+ : availableInputDevices.getDevicesFromHwModule(primaryOutput->getModuleHandle());
sp<DeviceDescriptor> device;
// when a call is active, force device selection to match source VOICE_COMMUNICATION
@@ -514,6 +520,7 @@
if ((getPhoneState() == AUDIO_MODE_IN_CALL) &&
(availableOutputDevices.getDevice(AUDIO_DEVICE_OUT_TELEPHONY_TX,
String8(""), AUDIO_FORMAT_DEFAULT)) == nullptr) {
+ LOG_ALWAYS_FATAL_IF(availablePrimaryDevices.isEmpty(), "Primary devices not found");
availableDevices = availablePrimaryDevices;
}
@@ -544,6 +551,9 @@
case AUDIO_SOURCE_UNPROCESSED:
case AUDIO_SOURCE_HOTWORD:
if (inputSource == AUDIO_SOURCE_HOTWORD) {
+ // We should not use primary output criteria for Hotword but rather limit
+ // to devices attached to the same HW module as the build in mic
+ LOG_ALWAYS_FATAL_IF(availablePrimaryDevices.isEmpty(), "Primary devices not found");
availableDevices = availablePrimaryDevices;
}
if (getForceUse(AUDIO_POLICY_FORCE_FOR_RECORD) == AUDIO_POLICY_FORCE_BT_SCO) {
diff --git a/services/audiopolicy/enginedefault/src/Engine.h b/services/audiopolicy/enginedefault/src/Engine.h
index 4360c6f..bb9e2df 100644
--- a/services/audiopolicy/enginedefault/src/Engine.h
+++ b/services/audiopolicy/enginedefault/src/Engine.h
@@ -39,6 +39,7 @@
STRATEGY_TRANSMITTED_THROUGH_SPEAKER,
STRATEGY_ACCESSIBILITY,
STRATEGY_REROUTING,
+ STRATEGY_CALL_ASSISTANT,
};
class Engine : public EngineBase
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 b427794..75c89aa 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>
@@ -682,8 +681,8 @@
* Switching to or from incall state or switching between telephony and VoIP lead to force
* routing command.
*/
- bool force = ((is_state_in_call(oldState) != is_state_in_call(state))
- || (is_state_in_call(state) && (state != oldState)));
+ bool force = ((isStateInCall(oldState) != isStateInCall(state))
+ || (isStateInCall(state) && (state != oldState)));
// check for device and output changes triggered by new phone state
checkForDeviceAndOutputChanges();
@@ -929,7 +928,8 @@
audio_output_flags_t *flags,
audio_port_handle_t *selectedDeviceId,
bool *isRequestedDeviceForExclusiveUse,
- std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs)
+ std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs,
+ output_type_t *outputType)
{
DeviceVector outputDevices;
const audio_port_handle_t requestedPortId = *selectedDeviceId;
@@ -937,6 +937,7 @@
const sp<DeviceDescriptor> requestedDevice =
mAvailableOutputDevices.getDeviceFromId(requestedPortId);
+ *outputType = API_OUTPUT_INVALID;
status_t status = getAudioAttributes(resultAttr, attr, *stream);
if (status != NO_ERROR) {
return status;
@@ -970,13 +971,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;
- ALOGV("getOutputForAttr() returns output %d", *output);
- return NO_ERROR;
+ 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;
+ }
+ return NO_ERROR;
+ }
}
// Virtual sources must always be dynamicaly or explicitly routed
if (resultAttr->usage == AUDIO_USAGE_VIRTUAL_SOURCE) {
@@ -999,7 +1008,7 @@
if (outputDevices.onlyContainsDevicesWithType(AUDIO_DEVICE_OUT_TELEPHONY_TX) &&
(*stream == AUDIO_STREAM_MUSIC || resultAttr->usage == AUDIO_USAGE_VOICE_COMMUNICATION) &&
audio_is_linear_pcm(config->format) &&
- isInCall()) {
+ isCallAudioAccessible()) {
if (requestedPortId != AUDIO_PORT_HANDLE_NONE) {
*flags = (audio_output_flags_t)AUDIO_OUTPUT_FLAG_INCALL_MUSIC;
*isRequestedDeviceForExclusiveUse = true;
@@ -1032,6 +1041,12 @@
*selectedDeviceId = getFirstDeviceId(outputDevices);
+ if (outputDevices.onlyContainsDevicesWithType(AUDIO_DEVICE_OUT_TELEPHONY_TX)) {
+ *outputType = API_OUTPUT_TELEPHONY_TX;
+ } else {
+ *outputType = API_OUTPUT_LEGACY;
+ }
+
ALOGV("%s returns output %d selectedDeviceId %d", __func__, *output, *selectedDeviceId);
return NO_ERROR;
@@ -1046,7 +1061,8 @@
audio_output_flags_t *flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId,
- std::vector<audio_io_handle_t> *secondaryOutputs)
+ std::vector<audio_io_handle_t> *secondaryOutputs,
+ output_type_t *outputType)
{
// The supplied portId must be AUDIO_PORT_HANDLE_NONE
if (*portId != AUDIO_PORT_HANDLE_NONE) {
@@ -1066,7 +1082,7 @@
status_t status = getOutputForAttrInt(&resultAttr, output, session, attr, stream, uid,
config, flags, selectedDeviceId, &isRequestedDeviceForExclusiveUse,
- &secondaryOutputDescs);
+ &secondaryOutputDescs, outputType);
if (status != NO_ERROR) {
return status;
}
@@ -1632,7 +1648,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) {
@@ -1814,7 +1830,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,
@@ -2261,7 +2277,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);
@@ -2270,7 +2286,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
@@ -2278,7 +2294,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;
@@ -2325,7 +2341,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);
@@ -2335,7 +2351,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;
@@ -2353,7 +2369,7 @@
DeviceVector primaryInputDevices = availablePrimaryModuleInputDevices();
if (primaryInputDevices.contains(inputDesc->getDevice()) &&
mInputs.activeInputsCountOnDevices(primaryInputDevices) == 0) {
- SoundTrigger::setCaptureState(false);
+ mpClientInterface->setSoundTriggerCaptureState(false);
}
inputDesc->clearPreemptedSessions();
}
@@ -2756,12 +2772,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,
@@ -2781,16 +2799,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);
@@ -3967,10 +3975,11 @@
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);
+ &secondaryOutputs, &outputType);
if (output == AUDIO_IO_HANDLE_NONE) {
ALOGV("%s no output for device %s",
__FUNCTION__, dumpDeviceTypes(sinkDevices.types()).c_str());
@@ -4240,11 +4249,11 @@
return profileUpdated ? NO_ERROR : INVALID_OPERATION;
}
-void AudioPolicyManager::setAppState(uid_t uid, app_state_t state)
+void AudioPolicyManager::setAppState(audio_port_handle_t portId, app_state_t state)
{
- ALOGV("%s(uid:%d, state:%d)", __func__, uid, state);
+ ALOGV("%s(portId:%d, state:%d)", __func__, portId, state);
for (size_t i = 0; i < mInputs.size(); i++) {
- mInputs.valueAt(i)->setAppState(uid, state);
+ mInputs.valueAt(i)->setAppState(portId, state);
}
}
@@ -4265,6 +4274,12 @@
return false;
}
+bool AudioPolicyManager::isCallScreenModeSupported()
+{
+ return getConfig().isCallScreenModeSupported();
+}
+
+
status_t AudioPolicyManager::disconnectAudioSource(const sp<SourceClientDescriptor>& sourceDesc)
{
ALOGV("%s port Id %d", __FUNCTION__, sourceDesc->portId());
@@ -5030,8 +5045,6 @@
setMsdPatch();
}
}
-
- cleanUpEffectsForIo(output);
}
void AudioPolicyManager::closeInput(audio_io_handle_t input)
@@ -5061,10 +5074,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(
@@ -5121,10 +5132,12 @@
// also take into account external policy-related changes: add all outputs which are
// associated with policies in the "before" and "after" output vectors
ALOGVV("%s(): policy related outputs", __func__);
+ bool hasDynamicPolicy = false;
for (size_t i = 0 ; i < mPreviousOutputs.size() ; i++) {
const sp<SwAudioOutputDescriptor> desc = mPreviousOutputs.valueAt(i);
if (desc != 0 && desc->mPolicyMix != NULL) {
srcOutputs.add(desc->mIoHandle);
+ hasDynamicPolicy = true;
ALOGVV(" previous outputs: adding %d", desc->mIoHandle);
}
}
@@ -5132,6 +5145,7 @@
const sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
if (desc != 0 && desc->mPolicyMix != NULL) {
dstOutputs.add(desc->mIoHandle);
+ hasDynamicPolicy = true;
ALOGVV(" new outputs: adding %d", desc->mIoHandle);
}
}
@@ -5140,12 +5154,45 @@
// get maximum latency of all source outputs to determine the minimum mute time guaranteeing
// audio from invalidated tracks will be rendered when unmuting
uint32_t maxLatency = 0;
+ bool invalidate = hasDynamicPolicy;
for (audio_io_handle_t srcOut : srcOutputs) {
sp<SwAudioOutputDescriptor> desc = mPreviousOutputs.valueFor(srcOut);
- if (desc != 0 && maxLatency < desc->latency()) {
+ if (desc == nullptr) continue;
+
+ if (desc->isStrategyActive(psId) && maxLatency < desc->latency()) {
maxLatency = desc->latency();
}
+
+ if (invalidate) continue;
+
+ for (auto client : desc->clientsList(false /*activeOnly*/)) {
+ if (desc->isDuplicated() || !desc->mProfile->isDirectOutput()) {
+ // a client on a non direct outputs has necessarily a linear PCM format
+ // so we can call selectOutput() safely
+ const audio_io_handle_t newOutput = selectOutput(dstOutputs,
+ client->flags(),
+ client->config().format,
+ client->config().channel_mask,
+ client->config().sample_rate);
+ if (newOutput != srcOut) {
+ invalidate = true;
+ break;
+ }
+ } else {
+ sp<IOProfile> profile = getProfileForOutput(newDevices,
+ client->config().sample_rate,
+ client->config().format,
+ client->config().channel_mask,
+ client->flags(),
+ true /* directOnly */);
+ if (profile != desc->mProfile) {
+ invalidate = true;
+ break;
+ }
+ }
+ }
}
+
ALOGV_IF(!(srcOutputs.isEmpty() || dstOutputs.isEmpty()),
"%s: strategy %d, moving from output %s to output %s", __func__, psId,
std::to_string(srcOutputs[0]).c_str(),
@@ -5153,7 +5200,9 @@
// mute strategy while moving tracks from one output to another
for (audio_io_handle_t srcOut : srcOutputs) {
sp<SwAudioOutputDescriptor> desc = mPreviousOutputs.valueFor(srcOut);
- if (desc != 0 && desc->isStrategyActive(psId)) {
+ if (desc == nullptr) continue;
+
+ if (desc->isStrategyActive(psId)) {
setStrategyMute(psId, true, desc);
setStrategyMute(psId, false, desc, maxLatency * LATENCY_MUTE_FACTOR,
newDevices.types());
@@ -5169,8 +5218,10 @@
selectOutputForMusicEffects();
}
// Move tracks associated to this stream (and linked) from previous output to new output
- for (auto stream : mEngine->getStreamTypesForProductStrategy(psId)) {
- mpClientInterface->invalidateStream(stream);
+ if (invalidate) {
+ for (auto stream : mEngine->getStreamTypesForProductStrategy(psId)) {
+ mpClientInterface->invalidateStream(stream);
+ }
}
}
}
@@ -5388,6 +5439,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:
@@ -6060,6 +6140,7 @@
case AUDIO_USAGE_GAME:
case AUDIO_USAGE_VIRTUAL_SOURCE:
case AUDIO_USAGE_ASSISTANT:
+ case AUDIO_USAGE_CALL_ASSISTANT:
break;
default:
return false;
@@ -6082,6 +6163,14 @@
return is_state_in_call(state);
}
+bool AudioPolicyManager::isCallAudioAccessible()
+{
+ audio_mode_t mode = mEngine->getPhoneState();
+ return (mode == AUDIO_MODE_IN_CALL)
+ || (mode == AUDIO_MODE_IN_COMMUNICATION)
+ || (mode == AUDIO_MODE_CALL_SCREEN);
+}
+
void AudioPolicyManager::cleanUpForDevice(const sp<DeviceDescriptor>& deviceDesc)
{
for (ssize_t i = (ssize_t)mAudioSources.size() - 1; i >= 0; i--) {
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index d516d7b..7e0e16f 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -121,7 +121,8 @@
audio_output_flags_t *flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId,
- std::vector<audio_io_handle_t> *secondaryOutputs) override;
+ std::vector<audio_io_handle_t> *secondaryOutputs,
+ output_type_t *outputType) override;
virtual status_t startOutput(audio_port_handle_t portId);
virtual status_t stopOutput(audio_port_handle_t portId);
virtual void releaseOutput(audio_port_handle_t portId);
@@ -192,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,
@@ -284,7 +289,7 @@
virtual status_t getHwOffloadEncodingFormatsSupportedForA2DP(
std::vector<audio_format_t> *formats);
- virtual void setAppState(uid_t uid, app_state_t state);
+ virtual void setAppState(audio_port_handle_t portId, app_state_t state);
virtual bool isHapticPlaybackSupported();
@@ -312,6 +317,8 @@
return volumeGroup != VOLUME_GROUP_NONE ? NO_ERROR : BAD_VALUE;
}
+ bool isCallScreenModeSupported() override;
+
status_t initialize();
protected:
@@ -352,7 +359,7 @@
}
virtual const DeviceVector getAvailableOutputDevices() const
{
- return mAvailableOutputDevices;
+ return mAvailableOutputDevices.filterForEngine();
}
virtual const DeviceVector getAvailableInputDevices() const
{
@@ -482,6 +489,8 @@
virtual bool isInCall();
// true if given state represents a device in a telephony or VoIP call
virtual bool isStateInCall(int state);
+ // true if playback to call TX or capture from call RX is possible
+ bool isCallAudioAccessible();
// when a device is connected, checks if an open output can be routed
// to this device. If none is open, tries to open one of the available outputs.
@@ -817,7 +826,8 @@
audio_output_flags_t *flags,
audio_port_handle_t *selectedDeviceId,
bool *isRequestedDeviceForExclusiveUse,
- std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs);
+ std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs,
+ output_type_t *outputType);
// internal method to return the output handle for the given device and format
audio_io_handle_t getOutputForDevices(
const DeviceVector &devices,
@@ -880,8 +890,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..80f4eab 100644
--- a/services/audiopolicy/service/Android.mk
+++ b/services/audiopolicy/service/Android.mk
@@ -31,7 +31,8 @@
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/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index c1190be..68a2a8c 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -19,7 +19,7 @@
#include "AudioPolicyService.h"
#include "TypeConverter.h"
-#include <media/MediaAnalyticsItem.h>
+#include <media/MediaMetricsItem.h>
#include <media/AudioPolicy.h>
#include <utils/Log.h>
@@ -181,13 +181,13 @@
if (mAudioPolicyManager == NULL) {
return NO_INIT;
}
- ALOGV("getOutputForAttr()");
+ ALOGV("%s()", __func__);
Mutex::Autolock _l(mLock);
const uid_t callingUid = IPCThreadState::self()->getCallingUid();
if (!isAudioServerOrMediaServerUid(callingUid) || uid == (uid_t)-1) {
ALOGW_IF(uid != (uid_t)-1 && uid != callingUid,
- "%s uid %d tried to pass itself off as %d", __FUNCTION__, callingUid, uid);
+ "%s uid %d tried to pass itself off as %d", __func__, callingUid, uid);
uid = callingUid;
}
if (!mPackageManager.allowPlaybackCapture(uid)) {
@@ -197,32 +197,44 @@
&& !bypassInterruptionPolicyAllowed(pid, uid)) {
attr->flags &= ~(AUDIO_FLAG_BYPASS_INTERRUPTION_POLICY|AUDIO_FLAG_BYPASS_MUTE);
}
- audio_output_flags_t originalFlags = flags;
AutoCallerClear acc;
+ AudioPolicyInterface::output_type_t outputType;
status_t result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid,
config,
&flags, selectedDeviceId, portId,
- secondaryOutputs);
+ secondaryOutputs,
+ &outputType);
// FIXME: Introduce a way to check for the the telephony device before opening the output
- if ((result == NO_ERROR) &&
- (flags & AUDIO_OUTPUT_FLAG_INCALL_MUSIC) &&
- !modifyPhoneStateAllowed(pid, uid)) {
- // If the app tries to play music through the telephony device and doesn't have permission
- // the fallback to the default output device.
- mAudioPolicyManager->releaseOutput(*portId);
- flags = originalFlags;
- *selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
- *portId = AUDIO_PORT_HANDLE_NONE;
- secondaryOutputs->clear();
- result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid, config,
- &flags, selectedDeviceId, portId,
- secondaryOutputs);
+ if (result == NO_ERROR) {
+ // enforce permission (if any) required for each type of input
+ switch (outputType) {
+ case AudioPolicyInterface::API_OUTPUT_LEGACY:
+ break;
+ case AudioPolicyInterface::API_OUTPUT_TELEPHONY_TX:
+ if (!modifyPhoneStateAllowed(pid, uid)) {
+ ALOGE("%s() permission denied: modify phone state not allowed for uid %d",
+ __func__, uid);
+ result = PERMISSION_DENIED;
+ }
+ break;
+ case AudioPolicyInterface::API_OUT_MIX_PLAYBACK:
+ if (!modifyAudioRoutingAllowed(pid, uid)) {
+ ALOGE("%s() permission denied: modify audio routing not allowed for uid %d",
+ __func__, uid);
+ result = PERMISSION_DENIED;
+ }
+ break;
+ case AudioPolicyInterface::API_OUTPUT_INVALID:
+ default:
+ LOG_ALWAYS_FATAL("%s() encountered an invalid output type %d",
+ __func__, (int)outputType);
+ }
}
if (result == NO_ERROR) {
sp <AudioPlaybackClient> client =
- new AudioPlaybackClient(*attr, *output, uid, pid, session, *selectedDeviceId, *stream);
+ new AudioPlaybackClient(*attr, *output, uid, pid, session, *portId, *selectedDeviceId, *stream);
mAudioPlaybackClients.add(*portId, client);
}
return result;
@@ -382,8 +394,10 @@
pid = callingPid;
}
- // check calling permissions
- if (!recordingAllowed(opPackageName, pid, uid)) {
+ // check calling permissions.
+ // Capturing from FM_TUNER source is controlled by captureAudioOutputAllowed() only as this
+ // does not affect users privacy as does capturing from an actual microphone.
+ if (!(recordingAllowed(opPackageName, pid, uid) || attr->source == AUDIO_SOURCE_FM_TUNER)) {
ALOGE("%s permission denied: recording not allowed for uid %d pid %d",
__func__, uid, pid);
return PERMISSION_DENIED;
@@ -393,7 +407,8 @@
if ((inputSource == AUDIO_SOURCE_VOICE_UPLINK ||
inputSource == AUDIO_SOURCE_VOICE_DOWNLINK ||
inputSource == AUDIO_SOURCE_VOICE_CALL ||
- inputSource == AUDIO_SOURCE_ECHO_REFERENCE) &&
+ inputSource == AUDIO_SOURCE_ECHO_REFERENCE||
+ inputSource == AUDIO_SOURCE_FM_TUNER) &&
!canCaptureOutput) {
return PERMISSION_DENIED;
}
@@ -436,7 +451,7 @@
}
break;
case AudioPolicyInterface::API_INPUT_MIX_EXT_POLICY_REROUTE:
- if (!modifyAudioRoutingAllowed()) {
+ if (!modifyAudioRoutingAllowed(pid, uid)) {
ALOGE("getInputForAttr() permission denied: modify audio routing not allowed");
status = PERMISSION_DENIED;
}
@@ -456,7 +471,7 @@
return status;
}
- sp<AudioRecordClient> client = new AudioRecordClient(*attr, *input, uid, pid, session,
+ sp<AudioRecordClient> client = new AudioRecordClient(*attr, *input, uid, pid, session, *portId,
*selectedDeviceId, opPackageName,
canCaptureOutput, canCaptureHotword);
mAudioRecordClients.add(*portId, client);
@@ -499,7 +514,8 @@
}
// check calling permissions
- if (!startRecording(client->opPackageName, client->pid, client->uid)) {
+ if (!(startRecording(client->opPackageName, client->pid, client->uid)
+ || client->attributes.source == AUDIO_SOURCE_FM_TUNER)) {
ALOGE("%s permission denied: recording not allowed for uid %d pid %d",
__func__, client->uid, client->pid);
return PERMISSION_DENIED;
@@ -537,7 +553,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);
@@ -790,6 +806,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
@@ -1330,6 +1357,17 @@
return NO_ERROR;
}
+bool AudioPolicyService::isCallScreenModeSupported()
+{
+ if (mAudioPolicyManager == NULL) {
+ ALOGW("%s, mAudioPolicyManager == NULL", __func__);
+ return false;
+ }
+ Mutex::Autolock _l(mLock);
+ AutoCallerClear acc;
+ return mAudioPolicyManager->isCallScreenModeSupported();
+}
+
status_t AudioPolicyService::setPreferredDeviceForStrategy(product_strategy_t strategy,
const AudioDeviceTypeAddr &device)
{
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 90939ce..c83512f 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -406,8 +406,7 @@
{
// Go over all active clients and allow capture (does not force silence) in the
// following cases:
-// Another client in the same UID has already been allowed to capture
-// OR The client is the assistant
+// The client is the assistant
// AND an accessibility service is on TOP or a RTT call is active
// AND the source is VOICE_RECOGNITION or HOTWORD
// OR uses VOICE_RECOGNITION AND is on TOP
@@ -423,12 +422,18 @@
// AND is on TOP
// AND the source is VOICE_RECOGNITION or HOTWORD
// OR the client source is virtual (remote submix, call audio TX or RX...)
+// OR the client source is HOTWORD
+// AND is on TOP
+// OR all active clients are using HOTWORD source
+// AND no call is active
+// OR client has CAPTURE_AUDIO_OUTPUT privileged permission
// OR Any client
// AND The assistant is not on TOP
// AND is on TOP or latest started
// AND there is no active privacy sensitive capture or call
// OR client has CAPTURE_AUDIO_OUTPUT privileged permission
+
sp<AudioRecordClient> topActive;
sp<AudioRecordClient> latestActive;
sp<AudioRecordClient> latestSensitiveActive;
@@ -443,6 +448,7 @@
bool rttCallActive =
(mPhoneState == AUDIO_MODE_IN_CALL || mPhoneState == AUDIO_MODE_IN_COMMUNICATION)
&& mUidPolicy->isRttEnabled();
+ bool onlyHotwordActive = true;
// if Sensor Privacy is enabled then all recordings should be silenced.
if (mSensorPrivacyPolicy->isSensorPrivacyEnabled()) {
@@ -474,23 +480,25 @@
isAssistantOnTop = true;
}
}
- // Assistant capturing for HOTWORD or Accessibility services not considered
+ // Client capturing for HOTWORD or Accessibility services not considered
// for latest active to avoid masking regular clients started before
if (current->startTimeNs > latestStartNs
- && !((current->attributes.source == AUDIO_SOURCE_HOTWORD
- || isA11yOnTop || rttCallActive)
- && isAssistant)
+ && !(current->attributes.source == AUDIO_SOURCE_HOTWORD
+ || ((isA11yOnTop || rttCallActive) && isAssistant))
&& !isAccessibility) {
latestActive = current;
latestStartNs = current->startTimeNs;
}
- if (isPrivacySensitiveSource(current->attributes.source)) {
+ if ((current->attributes.flags & AUDIO_FLAG_CAPTURE_PRIVATE) != 0) {
if (current->startTimeNs > latestSensitiveStartNs) {
latestSensitiveActive = current;
latestSensitiveStartNs = current->startTimeNs;
}
isSensitiveActive = true;
}
+ if (current->attributes.source != AUDIO_SOURCE_HOTWORD) {
+ onlyHotwordActive = false;
+ }
}
// if no active client with UI on Top, consider latest active as top
@@ -498,21 +506,12 @@
topActive = latestActive;
}
- std::vector<uid_t> enabledUids;
-
for (size_t i =0; i < mAudioRecordClients.size(); i++) {
sp<AudioRecordClient> current = mAudioRecordClients[i];
if (!current->active) {
continue;
}
- // keep capture allowed if another client with the same UID has already
- // been allowed to capture
- if (std::find(enabledUids.begin(), enabledUids.end(), current->uid)
- != enabledUids.end()) {
- continue;
- }
-
audio_source_t source = current->attributes.source;
bool isTopOrLatestActive = topActive == nullptr ? false : current->uid == topActive->uid;
bool isLatestSensitive = latestSensitiveActive == nullptr ?
@@ -552,29 +551,32 @@
}
} else if (mUidPolicy->isA11yUid(current->uid)) {
// For accessibility service allow capture if:
- // Is on TOP
- // AND the source is VOICE_RECOGNITION or HOTWORD
- // Or
- // The assistant is not on TOP
- // AND there is no active privacy sensitive capture or call
+ // The assistant is not on TOP
+ // AND there is no active privacy sensitive capture or call
// OR client has CAPTURE_AUDIO_OUTPUT privileged permission
+ // OR
+ // Is on TOP AND the source is VOICE_RECOGNITION or HOTWORD
+ if (!isAssistantOnTop
+ && (!(isSensitiveActive || isInCall) || current->canCaptureOutput)) {
+ allowCapture = true;
+ }
if (isA11yOnTop) {
if (source == AUDIO_SOURCE_VOICE_RECOGNITION || source == AUDIO_SOURCE_HOTWORD) {
allowCapture = true;
}
- } else {
- if (!isAssistantOnTop
- && (!(isSensitiveActive || isInCall) || current->canCaptureOutput)) {
- allowCapture = true;
- }
+ }
+ } else if (source == AUDIO_SOURCE_HOTWORD) {
+ // For HOTWORD source allow capture when not on TOP if:
+ // All active clients are using HOTWORD source
+ // AND no call is active
+ // OR client has CAPTURE_AUDIO_OUTPUT privileged permission
+ if (onlyHotwordActive && !(isInCall && !current->canCaptureOutput)) {
+ allowCapture = true;
}
}
- setAppState_l(current->uid,
+ setAppState_l(current->portId,
allowCapture ? apmStatFromAmState(mUidPolicy->getUidState(current->uid)) :
APP_STATE_IDLE);
- if (allowCapture) {
- enabledUids.push_back(current->uid);
- }
}
}
@@ -582,7 +584,7 @@
for (size_t i = 0; i < mAudioRecordClients.size(); i++) {
sp<AudioRecordClient> current = mAudioRecordClients[i];
if (!isVirtualSource(current->attributes.source)) {
- setAppState_l(current->uid, APP_STATE_IDLE);
+ setAppState_l(current->portId, APP_STATE_IDLE);
}
}
}
@@ -600,19 +602,6 @@
}
/* static */
-bool AudioPolicyService::isPrivacySensitiveSource(audio_source_t source)
-{
- switch (source) {
- case AUDIO_SOURCE_CAMCORDER:
- case AUDIO_SOURCE_VOICE_COMMUNICATION:
- return true;
- default:
- break;
- }
- return false;
-}
-
-/* static */
bool AudioPolicyService::isVirtualSource(audio_source_t source)
{
switch (source) {
@@ -628,17 +617,17 @@
return false;
}
-void AudioPolicyService::setAppState_l(uid_t uid, app_state_t state)
+void AudioPolicyService::setAppState_l(audio_port_handle_t portId, app_state_t state)
{
AutoCallerClear acc;
if (mAudioPolicyManager) {
- mAudioPolicyManager->setAppState(uid, state);
+ mAudioPolicyManager->setAppState(portId, state);
}
sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
if (af) {
bool silenced = state == APP_STATE_IDLE;
- af->setRecordSilenced(uid, silenced);
+ af->setRecordSilenced(portId, silenced);
}
}
@@ -975,7 +964,8 @@
void AudioPolicyService::UidPolicy::onUidStateChanged(uid_t uid,
int32_t procState,
- int64_t procStateSeq __unused) {
+ int64_t procStateSeq __unused,
+ int32_t capability __unused) {
if (procState != ActivityManager::PROCESS_STATE_UNKNOWN) {
updateUid(&mCachedUids, uid, true, procState, true);
}
@@ -1040,8 +1030,7 @@
bool AudioPolicyService::UidPolicy::isA11yOnTop() {
for (const auto &uid : mCachedUids) {
- std::vector<uid_t>::iterator it = find(mA11yUids.begin(), mA11yUids.end(), uid.first);
- if (it == mA11yUids.end()) {
+ if (!isA11yUid(uid.first)) {
continue;
}
if (uid.second.second >= ActivityManager::PROCESS_STATE_TOP
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index 7b72dc1..84adcc2 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,
@@ -268,6 +270,8 @@
virtual status_t setRttEnabled(bool enabled);
+ bool isCallScreenModeSupported() override;
+
status_t doStopOutput(audio_port_handle_t portId);
void doReleaseOutput(audio_port_handle_t portId);
@@ -320,7 +324,7 @@
virtual status_t shellCommand(int in, int out, int err, Vector<String16>& args);
// Sets whether the given UID records only silence
- virtual void setAppState_l(uid_t uid, app_state_t state);
+ virtual void setAppState_l(audio_port_handle_t portId, app_state_t state);
// Overrides the UID state as if it is idle
status_t handleSetUidState(Vector<String16>& args, int err);
@@ -345,7 +349,6 @@
void silenceAllRecordings_l();
- static bool isPrivacySensitiveSource(audio_source_t source);
static bool isVirtualSource(audio_source_t source);
// If recording we need to make sure the UID is allowed to do that. If the UID is idle
@@ -380,7 +383,8 @@
void onUidActive(uid_t uid) override;
void onUidGone(uid_t uid, bool disabled) override;
void onUidIdle(uid_t uid, bool disabled) override;
- void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq);
+ void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq,
+ int32_t capability);
void addOverrideUid(uid_t uid, bool active) { updateOverrideUid(uid, active, true); }
void removeOverrideUid(uid_t uid) { updateOverrideUid(uid, false, false); }
@@ -715,6 +719,8 @@
virtual audio_unique_id_t newAudioUniqueId(audio_unique_id_use_t use);
+ void setSoundTriggerCaptureState(bool active) override;
+
private:
AudioPolicyService *mAudioPolicyService;
};
@@ -767,9 +773,10 @@
public:
AudioClient(const audio_attributes_t attributes,
const audio_io_handle_t io, uid_t uid, pid_t pid,
- const audio_session_t session, const audio_port_handle_t deviceId) :
+ const audio_session_t session, audio_port_handle_t portId,
+ const audio_port_handle_t deviceId) :
attributes(attributes), io(io), uid(uid), pid(pid),
- session(session), deviceId(deviceId), active(false) {}
+ session(session), portId(portId), deviceId(deviceId), active(false) {}
~AudioClient() override = default;
@@ -778,6 +785,7 @@
const uid_t uid; // client UID
const pid_t pid; // client PID
const audio_session_t session; // audio session ID
+ const audio_port_handle_t portId;
const audio_port_handle_t deviceId; // selected input device port ID
bool active; // Playback/Capture is active or inactive
};
@@ -789,10 +797,10 @@
public:
AudioRecordClient(const audio_attributes_t attributes,
const audio_io_handle_t io, uid_t uid, pid_t pid,
- const audio_session_t session, const audio_port_handle_t deviceId,
- const String16& opPackageName,
+ const audio_session_t session, audio_port_handle_t portId,
+ const audio_port_handle_t deviceId, const String16& opPackageName,
bool canCaptureOutput, bool canCaptureHotword) :
- AudioClient(attributes, io, uid, pid, session, deviceId),
+ AudioClient(attributes, io, uid, pid, session, portId, deviceId),
opPackageName(opPackageName), startTimeNs(0),
canCaptureOutput(canCaptureOutput), canCaptureHotword(canCaptureHotword) {}
~AudioRecordClient() override = default;
@@ -810,9 +818,9 @@
public:
AudioPlaybackClient(const audio_attributes_t attributes,
const audio_io_handle_t io, uid_t uid, pid_t pid,
- const audio_session_t session, audio_port_handle_t deviceId,
- audio_stream_type_t stream) :
- AudioClient(attributes, io, uid, pid, session, deviceId), stream(stream) {}
+ const audio_session_t session, audio_port_handle_t portId,
+ audio_port_handle_t deviceId, audio_stream_type_t stream) :
+ AudioClient(attributes, io, uid, pid, session, portId, deviceId), stream(stream) {}
~AudioPlaybackClient() override = default;
const audio_stream_type_t stream;
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/AudioPolicyTestManager.h b/services/audiopolicy/tests/AudioPolicyTestManager.h
index c77dcdc..bafcc63 100644
--- a/services/audiopolicy/tests/AudioPolicyTestManager.h
+++ b/services/audiopolicy/tests/AudioPolicyTestManager.h
@@ -26,6 +26,7 @@
using AudioPolicyManager::getConfig;
using AudioPolicyManager::loadConfig;
using AudioPolicyManager::initialize;
+ using AudioPolicyManager::getOutputs;
};
} // namespace android
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index 0263597..2a8349c 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -82,14 +82,15 @@
virtual void SetUpManagerConfig();
void dumpToLog();
- // When explicitly routing is needed, selectedDeviceId need to be set as the wanted port
- // id. Otherwise, selectedDeviceId need to be initialized as AUDIO_PORT_HANDLE_NONE.
+ // When explicit routing is needed, selectedDeviceId needs to be set as the wanted port
+ // id. Otherwise, selectedDeviceId needs to be initialized as AUDIO_PORT_HANDLE_NONE.
void getOutputForAttr(
audio_port_handle_t *selectedDeviceId,
audio_format_t format,
int channelMask,
int sampleRate,
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
+ audio_io_handle_t *output = nullptr,
audio_port_handle_t *portId = nullptr,
audio_attributes_t attr = {});
void getInputForAttr(
@@ -164,9 +165,12 @@
int channelMask,
int sampleRate,
audio_output_flags_t flags,
+ audio_io_handle_t *output,
audio_port_handle_t *portId,
audio_attributes_t attr) {
- audio_io_handle_t output = AUDIO_PORT_HANDLE_NONE;
+ audio_io_handle_t localOutput;
+ if (!output) output = &localOutput;
+ *output = AUDIO_IO_HANDLE_NONE;
audio_stream_type_t stream = AUDIO_STREAM_DEFAULT;
audio_config_t config = AUDIO_CONFIG_INITIALIZER;
config.sample_rate = sampleRate;
@@ -175,10 +179,12 @@
audio_port_handle_t localPortId;
if (!portId) portId = &localPortId;
*portId = AUDIO_PORT_HANDLE_NONE;
+ AudioPolicyInterface::output_type_t outputType;
ASSERT_EQ(OK, mManager->getOutputForAttr(
- &attr, &output, AUDIO_SESSION_NONE, &stream, 0 /*uid*/, &config, &flags,
- selectedDeviceId, portId, {}));
+ &attr, output, AUDIO_SESSION_NONE, &stream, 0 /*uid*/, &config, &flags,
+ selectedDeviceId, portId, {}, &outputType));
ASSERT_NE(AUDIO_PORT_HANDLE_NONE, *portId);
+ ASSERT_NE(AUDIO_IO_HANDLE_NONE, *output);
}
void AudioPolicyManagerTest::getInputForAttr(
@@ -228,7 +234,7 @@
return;
}
}
- GTEST_FAIL();
+ GTEST_FAIL() << "Device port with role " << role << " and address " << address << "not found";
}
audio_port_handle_t AudioPolicyManagerTest::getDeviceIdFromPatch(
@@ -438,7 +444,7 @@
audio_port_handle_t portId;
getOutputForAttr(&selectedDeviceId,
AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT,
- &portId);
+ nullptr /*output*/, &portId);
ASSERT_EQ(selectedDeviceId, mMsdOutputDevice->getId());
ASSERT_EQ(1, patchCount.deltaFromSnapshot());
mManager->releaseOutput(portId);
@@ -450,7 +456,7 @@
audio_port_handle_t portId;
getOutputForAttr(&selectedDeviceId,
AUDIO_FORMAT_DTS, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT,
- &portId);
+ nullptr /*output*/, &portId);
ASSERT_NE(selectedDeviceId, mMsdOutputDevice->getId());
ASSERT_EQ(-1, patchCount.deltaFromSnapshot());
mManager->releaseOutput(portId);
@@ -725,9 +731,9 @@
const audio_usage_t usage = attr.usage;
audio_port_handle_t playbackRoutedPortId = AUDIO_PORT_HANDLE_NONE;
- audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
getOutputForAttr(&playbackRoutedPortId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
- 48000 /*sampleRate*/, AUDIO_OUTPUT_FLAG_NONE, &portId, attr);
+ 48000 /*sampleRate*/, AUDIO_OUTPUT_FLAG_NONE,
+ nullptr /*output*/, nullptr /*portId*/, attr);
if (std::find_if(begin(mUsageRules), end(mUsageRules), [&usage](const auto &usageRule) {
return (std::get<0>(usageRule) == usage) &&
(std::get<2>(usageRule) == RULE_MATCH_ATTRIBUTE_USAGE);}) != end(mUsageRules) ||
@@ -884,7 +890,7 @@
std::string tags = std::string("addr=") + mMixAddress;
strncpy(attr.tags, tags.c_str(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
- 48000 /*sampleRate*/, AUDIO_OUTPUT_FLAG_NONE, &mPortId, attr);
+ 48000 /*sampleRate*/, AUDIO_OUTPUT_FLAG_NONE, nullptr /*output*/, &mPortId, attr);
ASSERT_EQ(NO_ERROR, mManager->startOutput(mPortId));
ASSERT_EQ(injectionPort.id, getDeviceIdFromPatch(mClient->getLastAddedPatch()));
@@ -1027,15 +1033,14 @@
findDevicePort(role, type, address, devicePort);
audio_port_handle_t routedPortId = devicePort.id;
- audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
// Try start input or output according to the device type
if (audio_is_output_devices(type)) {
getOutputForAttr(&routedPortId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
- 48000 /*sampleRate*/, AUDIO_OUTPUT_FLAG_NONE, &portId);
+ 48000 /*sampleRate*/, AUDIO_OUTPUT_FLAG_NONE);
} else if (audio_is_input_device(type)) {
RecordingActivityTracker tracker;
getInputForAttr({}, tracker.getRiid(), &routedPortId, AUDIO_FORMAT_PCM_16_BIT,
- AUDIO_CHANNEL_IN_STEREO, 48000 /*sampleRate*/, AUDIO_INPUT_FLAG_NONE, &portId);
+ AUDIO_CHANNEL_IN_STEREO, 48000 /*sampleRate*/, AUDIO_INPUT_FLAG_NONE);
}
ASSERT_EQ(devicePort.id, routedPortId);
@@ -1058,3 +1063,67 @@
"hfp_client_out"})
)
);
+
+class AudioPolicyManagerTVTest : public AudioPolicyManagerTestWithConfigurationFile {
+protected:
+ std::string getConfigFile() override { return sTvConfig; }
+ void testHDMIPortSelection(audio_output_flags_t flags, const char* expectedMixPortName);
+
+ static const std::string sTvConfig;
+};
+
+const std::string AudioPolicyManagerTVTest::sTvConfig =
+ AudioPolicyManagerTVTest::sExecutableDir + "test_tv_apm_configuration.xml";
+
+// SwAudioOutputDescriptor doesn't populate flags so check against the port name.
+void AudioPolicyManagerTVTest::testHDMIPortSelection(
+ audio_output_flags_t flags, const char* expectedMixPortName) {
+ ASSERT_EQ(NO_ERROR, mManager->setDeviceConnectionState(
+ AUDIO_DEVICE_OUT_AUX_DIGITAL, AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+ "" /*address*/, "" /*name*/, AUDIO_FORMAT_DEFAULT));
+ audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
+ audio_io_handle_t output;
+ audio_port_handle_t portId;
+ getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, 48000,
+ flags, &output, &portId);
+ sp<SwAudioOutputDescriptor> outDesc = mManager->getOutputs().valueFor(output);
+ ASSERT_NE(nullptr, outDesc.get());
+ audio_port port = {};
+ outDesc->toAudioPort(&port);
+ mManager->releaseOutput(portId);
+ ASSERT_EQ(NO_ERROR, mManager->setDeviceConnectionState(
+ AUDIO_DEVICE_OUT_AUX_DIGITAL, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+ "" /*address*/, "" /*name*/, AUDIO_FORMAT_DEFAULT));
+ ASSERT_EQ(AUDIO_PORT_TYPE_MIX, port.type);
+ ASSERT_EQ(AUDIO_PORT_ROLE_SOURCE, port.role);
+ ASSERT_STREQ(expectedMixPortName, port.name);
+}
+
+TEST_F(AudioPolicyManagerTVTest, InitSuccess) {
+ // SetUp must finish with no assertions.
+}
+
+TEST_F(AudioPolicyManagerTVTest, Dump) {
+ dumpToLog();
+}
+
+TEST_F(AudioPolicyManagerTVTest, MatchNoFlags) {
+ testHDMIPortSelection(AUDIO_OUTPUT_FLAG_NONE, "primary output");
+}
+
+TEST_F(AudioPolicyManagerTVTest, MatchOutputDirectNoHwAvSync) {
+ // b/140447125: The selected port must not have HW AV Sync flag (see the config file).
+ testHDMIPortSelection(AUDIO_OUTPUT_FLAG_DIRECT, "direct");
+}
+
+TEST_F(AudioPolicyManagerTVTest, MatchOutputDirectHwAvSync) {
+ testHDMIPortSelection(static_cast<audio_output_flags_t>(
+ AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_HW_AV_SYNC),
+ "tunnel");
+}
+
+TEST_F(AudioPolicyManagerTVTest, MatchOutputDirectMMapNoIrq) {
+ testHDMIPortSelection(static_cast<audio_output_flags_t>(
+ AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_MMAP_NOIRQ),
+ "low latency");
+}
diff --git a/services/audiopolicy/tests/resources/Android.bp b/services/audiopolicy/tests/resources/Android.bp
index 41f5ee1..d9476d9 100644
--- a/services/audiopolicy/tests/resources/Android.bp
+++ b/services/audiopolicy/tests/resources/Android.bp
@@ -3,5 +3,6 @@
srcs: [
"test_audio_policy_configuration.xml",
"test_audio_policy_primary_only_configuration.xml",
+ "test_tv_apm_configuration.xml",
],
}
diff --git a/services/audiopolicy/tests/resources/test_tv_apm_configuration.xml b/services/audiopolicy/tests/resources/test_tv_apm_configuration.xml
new file mode 100644
index 0000000..f1638f3
--- /dev/null
+++ b/services/audiopolicy/tests/resources/test_tv_apm_configuration.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- 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.
+-->
+
+<audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <globalConfiguration speaker_drc_enabled="false"/>
+ <modules>
+ <module name="primary" halVersion="2.0">
+ <attachedDevices>
+ <item>Speaker</item>
+ </attachedDevices>
+ <defaultOutputDevice>Speaker</defaultOutputDevice>
+ <mixPorts>
+ <!-- Profiles on the HDMI port are explicit for simplicity. In reality they are dynamic -->
+ <!-- Note: ports are intentionally arranged from more specific to less
+ specific in order to test b/140447125 for HW AV Sync, and similar "explicit matches" -->
+ <mixPort name="tunnel" role="source"
+ flags="AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_HW_AV_SYNC">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="low latency" role="source"
+ flags="AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_MMAP_NOIRQ">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="direct" role="source" flags="AUDIO_OUTPUT_FLAG_DIRECT">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="primary output" role="source" flags="AUDIO_OUTPUT_FLAG_PRIMARY">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ </mixPorts>
+ <devicePorts>
+ <devicePort tagName="Speaker" type="AUDIO_DEVICE_OUT_SPEAKER" role="sink" />
+ <devicePort tagName="Out Aux Digital" type="AUDIO_DEVICE_OUT_AUX_DIGITAL" role="sink" />
+ </devicePorts>
+ <routes>
+ <route type="mix" sink="Speaker" sources="primary output"/>
+ <route type="mix" sink="Out Aux Digital" sources="primary output,tunnel,direct,low latency"/>
+ </routes>
+ </module>
+ </modules>
+</audioPolicyConfiguration>
diff --git a/services/camera/libcameraservice/Android.bp b/services/camera/libcameraservice/Android.bp
index c50a3c6..c63feb2 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",
@@ -45,6 +47,7 @@
"api2/HeicCompositeStream.cpp",
"device1/CameraHardwareInterface.cpp",
"device3/Camera3Device.cpp",
+ "device3/Camera3OfflineSession.cpp",
"device3/Camera3Stream.cpp",
"device3/Camera3IOStreamBase.cpp",
"device3/Camera3InputStream.cpp",
@@ -54,7 +57,9 @@
"device3/StatusTracker.cpp",
"device3/Camera3BufferManager.cpp",
"device3/Camera3StreamSplitter.cpp",
+ "device3/CoordinateMapper.cpp",
"device3/DistortionMapper.cpp",
+ "device3/ZoomRatioMapper.cpp",
"gui/RingBufferConsumer.cpp",
"utils/CameraThreadState.cpp",
"hidl/AidlCameraDeviceCallbacks.cpp",
@@ -87,10 +92,12 @@
"libmediautils",
"libcamera_client",
"libcamera_metadata",
+ "libdynamic_depth",
"libfmq",
"libgui",
"libhardware",
"libhidlbase",
+ "libimage_io",
"libjpeg",
"libmedia_codeclist",
"libmedia_omx",
@@ -98,6 +105,7 @@
"libsensorprivacy",
"libstagefright",
"libstagefright_foundation",
+ "libxml2",
"libyuv",
"android.frameworks.cameraservice.common@2.0",
"android.frameworks.cameraservice.service@2.0",
@@ -110,6 +118,7 @@
"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 +147,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 850b275..18ed3a5 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -117,7 +117,12 @@
// ----------------------------------------------------------------------------
+static const String16 sDumpPermission("android.permission.DUMP");
static const String16 sManageCameraPermission("android.permission.MANAGE_CAMERA");
+static const String16 sCameraPermission("android.permission.CAMERA");
+static const String16 sSystemCameraPermission("android.permission.SYSTEM_CAMERA");
+static const String16
+ sCameraSendSystemEventsPermission("android.permission.CAMERA_SEND_SYSTEM_EVENTS");
// Matches with PERCEPTIBLE_APP_ADJ in ProcessList.java
static constexpr int32_t kVendorClientScore = 200;
@@ -130,7 +135,9 @@
CameraService::CameraService() :
mEventLog(DEFAULT_EVENT_LOG_LENGTH),
mNumberOfCameras(0),
- mSoundRef(0), mInitialized(false) {
+ mNumberOfCamerasWithoutSystemCamera(0),
+ mSoundRef(0), mInitialized(false),
+ mAudioRestriction(hardware::camera2::ICameraDeviceUser::AUDIO_RESTRICTION_NONE) {
ALOGI("CameraService started (pid=%d)", getpid());
mServiceLockWrapper = std::make_shared<WaitableMutexWrapper>(&mServiceLock);
}
@@ -157,6 +164,7 @@
mUidPolicy->registerSelf();
mSensorPrivacyPolicy = new SensorPrivacyPolicy(this);
mSensorPrivacyPolicy->registerSelf();
+ mAppOps.setCameraAudioRestriction(mAudioRestriction);
sp<HidlCameraService> hcs = HidlCameraService::getInstance(this);
if (hcs->registerAsService() != android::OK) {
ALOGE("%s: Failed to register default android.frameworks.cameraservice.service@1.0",
@@ -242,7 +250,7 @@
Mutex::Autolock lock(mStatusListenerLock);
for (auto& i : mListenerList) {
- i.second->getListener()->onTorchStatusChanged(mapToInterface(status), String16{cameraId});
+ i->getListener()->onTorchStatusChanged(mapToInterface(status), String16{cameraId});
}
}
@@ -256,32 +264,62 @@
enumerateProviders();
}
-bool CameraService::isPublicallyHiddenSecureCamera(const String8& cameraId) {
+void CameraService::filterAPI1SystemCameraLocked(
+ const std::vector<std::string> &normalDeviceIds) {
+ mNormalDeviceIdsWithoutSystemCamera.clear();
+ for (auto &deviceId : normalDeviceIds) {
+ SystemCameraKind deviceKind = SystemCameraKind::PUBLIC;
+ if (getSystemCameraKind(String8(deviceId.c_str()), &deviceKind) != OK) {
+ ALOGE("%s: Invalid camera id %s, skipping", __FUNCTION__, deviceId.c_str());
+ continue;
+ }
+ if (deviceKind == SystemCameraKind::SYSTEM_ONLY_CAMERA) {
+ // All system camera ids will necessarily come after public camera
+ // device ids as per the HAL interface contract.
+ break;
+ }
+ mNormalDeviceIdsWithoutSystemCamera.push_back(deviceId);
+ }
+ ALOGV("%s: number of API1 compatible public cameras is %zu", __FUNCTION__,
+ mNormalDeviceIdsWithoutSystemCamera.size());
+}
+
+status_t CameraService::getSystemCameraKind(const String8& cameraId, SystemCameraKind *kind) const {
auto state = getCameraState(cameraId);
if (state != nullptr) {
- return state->isPublicallyHiddenSecureCamera();
+ *kind = state->getSystemCameraKind();
+ return OK;
}
// Hidden physical camera ids won't have CameraState
- return mCameraProviderManager->isPublicallyHiddenSecureCamera(cameraId.c_str());
+ return mCameraProviderManager->getSystemCameraKind(cameraId.c_str(), kind);
}
void CameraService::updateCameraNumAndIds() {
Mutex::Autolock l(mServiceLock);
- mNumberOfCameras = mCameraProviderManager->getCameraCount();
+ std::pair<int, int> systemAndNonSystemCameras = mCameraProviderManager->getCameraCount();
+ // Excludes hidden secure cameras
+ mNumberOfCameras =
+ systemAndNonSystemCameras.first + systemAndNonSystemCameras.second;
+ mNumberOfCamerasWithoutSystemCamera = systemAndNonSystemCameras.second;
mNormalDeviceIds =
mCameraProviderManager->getAPI1CompatibleCameraDeviceIds();
+ filterAPI1SystemCameraLocked(mNormalDeviceIds);
}
void CameraService::addStates(const String8 id) {
std::string cameraId(id.c_str());
hardware::camera::common::V1_0::CameraResourceCost cost;
status_t res = mCameraProviderManager->getResourceCost(cameraId, &cost);
+ SystemCameraKind deviceKind = SystemCameraKind::PUBLIC;
if (res != OK) {
ALOGE("Failed to query device resource cost: %s (%d)", strerror(-res), res);
return;
}
- bool isPublicallyHiddenSecureCamera =
- mCameraProviderManager->isPublicallyHiddenSecureCamera(id.string());
+ res = mCameraProviderManager->getSystemCameraKind(cameraId, &deviceKind);
+ if (res != OK) {
+ ALOGE("Failed to query device kind: %s (%d)", strerror(-res), res);
+ return;
+ }
std::set<String8> conflicting;
for (size_t i = 0; i < cost.conflictingDevices.size(); i++) {
conflicting.emplace(String8(cost.conflictingDevices[i].c_str()));
@@ -290,8 +328,7 @@
{
Mutex::Autolock lock(mCameraStatesLock);
mCameraStates.emplace(id, std::make_shared<CameraState>(id, cost.resourceCost,
- conflicting,
- isPublicallyHiddenSecureCamera));
+ conflicting, deviceKind));
}
if (mFlashlight->hasFlashUnit(id)) {
@@ -454,15 +491,31 @@
broadcastTorchModeStatus(cameraId, newStatus);
}
+static bool hasPermissionsForSystemCamera(int callingPid, int callingUid) {
+ return checkPermission(sSystemCameraPermission, callingPid, callingUid) &&
+ checkPermission(sCameraPermission, callingPid, callingUid);
+}
+
Status CameraService::getNumberOfCameras(int32_t type, int32_t* numCameras) {
ATRACE_CALL();
Mutex::Autolock l(mServiceLock);
+ bool hasSystemCameraPermissions =
+ hasPermissionsForSystemCamera(CameraThreadState::getCallingPid(),
+ CameraThreadState::getCallingUid());
switch (type) {
case CAMERA_TYPE_BACKWARD_COMPATIBLE:
- *numCameras = static_cast<int>(mNormalDeviceIds.size());
+ if (hasSystemCameraPermissions) {
+ *numCameras = static_cast<int>(mNormalDeviceIds.size());
+ } else {
+ *numCameras = static_cast<int>(mNormalDeviceIdsWithoutSystemCamera.size());
+ }
break;
case CAMERA_TYPE_ALL:
- *numCameras = mNumberOfCameras;
+ if (hasSystemCameraPermissions) {
+ *numCameras = mNumberOfCameras;
+ } else {
+ *numCameras = mNumberOfCamerasWithoutSystemCamera;
+ }
break;
default:
ALOGW("%s: Unknown camera type %d",
@@ -477,20 +530,31 @@
CameraInfo* cameraInfo) {
ATRACE_CALL();
Mutex::Autolock l(mServiceLock);
+ std::string cameraIdStr = cameraIdIntToStrLocked(cameraId);
+ if (shouldRejectSystemCameraConnection(String8(cameraIdStr.c_str()))) {
+ return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION, "Unable to retrieve camera"
+ "characteristics for system only device %s: ", cameraIdStr.c_str());
+ }
if (!mInitialized) {
return STATUS_ERROR(ERROR_DISCONNECTED,
"Camera subsystem is not available");
}
-
- if (cameraId < 0 || cameraId >= mNumberOfCameras) {
+ bool hasSystemCameraPermissions =
+ hasPermissionsForSystemCamera(CameraThreadState::getCallingPid(),
+ CameraThreadState::getCallingUid());
+ int cameraIdBound = mNumberOfCamerasWithoutSystemCamera;
+ if (hasSystemCameraPermissions) {
+ cameraIdBound = mNumberOfCameras;
+ }
+ if (cameraId < 0 || cameraId >= cameraIdBound) {
return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT,
"CameraId is not valid");
}
Status ret = Status::ok();
status_t err = mCameraProviderManager->getCameraInfo(
- cameraIdIntToStrLocked(cameraId), cameraInfo);
+ cameraIdStr.c_str(), cameraInfo);
if (err != OK) {
ret = STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
"Error retrieving camera info from device %d: %s (%d)", cameraId,
@@ -501,13 +565,20 @@
}
std::string CameraService::cameraIdIntToStrLocked(int cameraIdInt) {
- if (cameraIdInt < 0 || cameraIdInt >= static_cast<int>(mNormalDeviceIds.size())) {
+ const std::vector<std::string> *deviceIds = &mNormalDeviceIdsWithoutSystemCamera;
+ auto callingPid = CameraThreadState::getCallingPid();
+ auto callingUid = CameraThreadState::getCallingUid();
+ if (checkPermission(sSystemCameraPermission, callingPid, callingUid) ||
+ getpid() == callingPid) {
+ deviceIds = &mNormalDeviceIds;
+ }
+ if (cameraIdInt < 0 || cameraIdInt >= static_cast<int>(deviceIds->size())) {
ALOGE("%s: input id %d invalid: valid range (0, %zu)",
- __FUNCTION__, cameraIdInt, mNormalDeviceIds.size());
+ __FUNCTION__, cameraIdInt, deviceIds->size());
return std::string{};
}
- return mNormalDeviceIds[cameraIdInt];
+ return (*deviceIds)[cameraIdInt];
}
String8 CameraService::cameraIdIntToStr(int cameraIdInt) {
@@ -529,16 +600,13 @@
"Camera subsystem is not available");;
}
- if (shouldRejectHiddenCameraConnection(String8(cameraId))) {
- ALOGW("Attempting to retrieve characteristics for system-only camera id %s, rejected",
- String8(cameraId).string());
- return STATUS_ERROR_FMT(ERROR_DISCONNECTED,
- "No camera device with ID \"%s\" currently available",
- String8(cameraId).string());
-
+ if (shouldRejectSystemCameraConnection(String8(cameraId))) {
+ return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION, "Unable to retrieve camera"
+ "characteristics for system only device %s: ", String8(cameraId).string());
}
Status ret{};
+
status_t res = mCameraProviderManager->getCameraCharacteristics(
String8(cameraId).string(), cameraInfo);
if (res != OK) {
@@ -546,13 +614,21 @@
"characteristics for device %s: %s (%d)", String8(cameraId).string(),
strerror(-res), res);
}
-
+ SystemCameraKind deviceKind = SystemCameraKind::PUBLIC;
+ if (getSystemCameraKind(String8(cameraId), &deviceKind) != OK) {
+ ALOGE("%s: Invalid camera id %s, skipping", __FUNCTION__, String8(cameraId).string());
+ return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION, "Unable to retrieve camera kind "
+ "for device %s", String8(cameraId).string());
+ }
int callingPid = CameraThreadState::getCallingPid();
int callingUid = CameraThreadState::getCallingUid();
std::vector<int32_t> tagsRemoved;
- // If it's not calling from cameraserver, check the permission.
+ // If it's not calling from cameraserver, check the permission only if
+ // android.permission.CAMERA is required. If android.permission.SYSTEM_CAMERA was needed,
+ // it would've already been checked in shouldRejectSystemCameraConnection.
if ((callingPid != getpid()) &&
- !checkPermission(String16("android.permission.CAMERA"), callingPid, callingUid)) {
+ (deviceKind != SystemCameraKind::SYSTEM_ONLY_CAMERA) &&
+ !checkPermission(sCameraPermission, callingPid, callingUid)) {
res = cameraInfo->removePermissionEntries(
mCameraProviderManager->getProviderTagIdLocked(String8(cameraId).string()),
&tagsRemoved);
@@ -656,9 +732,10 @@
}
Status CameraService::makeClient(const sp<CameraService>& cameraService,
- const sp<IInterface>& cameraCb, const String16& packageName, const String8& cameraId,
- int api1CameraId, int facing, int clientPid, uid_t clientUid, int servicePid,
- int halVersion, int deviceVersion, apiLevel effectiveApiLevel,
+ const sp<IInterface>& cameraCb, const String16& packageName,
+ const std::unique_ptr<String16>& featureId, const String8& cameraId, int api1CameraId,
+ int facing, int clientPid, uid_t clientUid, int servicePid, int halVersion,
+ int deviceVersion, apiLevel effectiveApiLevel,
/*out*/sp<BasicClient>* client) {
if (halVersion < 0 || halVersion == deviceVersion) {
@@ -668,7 +745,7 @@
case CAMERA_DEVICE_API_VERSION_1_0:
if (effectiveApiLevel == API_1) { // Camera1 API route
sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
- *client = new CameraClient(cameraService, tmp, packageName,
+ *client = new CameraClient(cameraService, tmp, packageName, featureId,
api1CameraId, facing, clientPid, clientUid,
getpid());
} else { // Camera2 API route
@@ -686,15 +763,15 @@
case CAMERA_DEVICE_API_VERSION_3_5:
if (effectiveApiLevel == API_1) { // Camera1 API route
sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
- *client = new Camera2Client(cameraService, tmp, packageName,
+ *client = new Camera2Client(cameraService, tmp, packageName, featureId,
cameraId, api1CameraId,
facing, clientPid, clientUid,
servicePid);
} else { // Camera2 API route
sp<hardware::camera2::ICameraDeviceCallbacks> tmp =
static_cast<hardware::camera2::ICameraDeviceCallbacks*>(cameraCb.get());
- *client = new CameraDeviceClient(cameraService, tmp, packageName, cameraId,
- facing, clientPid, clientUid, servicePid);
+ *client = new CameraDeviceClient(cameraService, tmp, packageName, featureId,
+ cameraId, facing, clientPid, clientUid, servicePid);
}
break;
default:
@@ -711,7 +788,7 @@
halVersion == CAMERA_DEVICE_API_VERSION_1_0) {
// Only support higher HAL version device opened as HAL1.0 device.
sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
- *client = new CameraClient(cameraService, tmp, packageName,
+ *client = new CameraClient(cameraService, tmp, packageName, featureId,
api1CameraId, facing, clientPid, clientUid,
servicePid);
} else {
@@ -811,7 +888,7 @@
if (!(ret = connectHelper<ICameraClient,Client>(
sp<ICameraClient>{nullptr}, id, cameraId,
static_cast<int>(CAMERA_HAL_API_VERSION_UNSPECIFIED),
- internalPackageName, uid, USE_CALLING_PID,
+ internalPackageName, std::unique_ptr<String16>(), uid, USE_CALLING_PID,
API_1, /*shimUpdateOnly*/ true, /*out*/ tmp)
).isOk()) {
ALOGE("%s: Error initializing shim metadata: %s", __FUNCTION__, ret.toString8().string());
@@ -992,9 +1069,26 @@
clientName8.string(), clientUid, clientPid);
}
- // If it's not calling from cameraserver, check the permission.
+ if (shouldRejectSystemCameraConnection(cameraId)) {
+ ALOGW("Attempting to connect to system-only camera id %s, connection rejected",
+ cameraId.c_str());
+ return STATUS_ERROR_FMT(ERROR_DISCONNECTED, "No camera device with ID \"%s\" is"
+ "available", cameraId.string());
+ }
+ SystemCameraKind deviceKind = SystemCameraKind::PUBLIC;
+ if (getSystemCameraKind(cameraId, &deviceKind) != OK) {
+ ALOGE("%s: Invalid camera id %s, skipping", __FUNCTION__, cameraId.string());
+ return STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT, "No camera device with ID \"%s\""
+ "found while trying to query device kind", cameraId.string());
+
+ }
+
+ // If it's not calling from cameraserver, check the permission if the
+ // device isn't a system only camera (shouldRejectSystemCameraConnection already checks for
+ // android.permission.SYSTEM_CAMERA for system only camera devices).
if (callingPid != getpid() &&
- !checkPermission(String16("android.permission.CAMERA"), clientPid, clientUid)) {
+ (deviceKind != SystemCameraKind::SYSTEM_ONLY_CAMERA) &&
+ !checkPermission(sCameraPermission, clientPid, clientUid)) {
ALOGE("Permission Denial: can't use the camera pid=%d, uid=%d", clientPid, clientUid);
return STATUS_ERROR_FMT(ERROR_PERMISSION_DENIED,
"Caller \"%s\" (PID %d, UID %d) cannot open camera \"%s\" without camera permission",
@@ -1307,8 +1401,8 @@
String8 id = cameraIdIntToStr(api1CameraId);
sp<Client> client = nullptr;
ret = connectHelper<ICameraClient,Client>(cameraClient, id, api1CameraId,
- CAMERA_HAL_API_VERSION_UNSPECIFIED, clientPackageName, clientUid, clientPid, API_1,
- /*shimUpdateOnly*/ false, /*out*/client);
+ CAMERA_HAL_API_VERSION_UNSPECIFIED, clientPackageName, std::unique_ptr<String16>(),
+ clientUid, clientPid, API_1, /*shimUpdateOnly*/ false, /*out*/client);
if(!ret.isOk()) {
logRejected(id, CameraThreadState::getCallingPid(), String8(clientPackageName),
@@ -1334,8 +1428,8 @@
Status ret = Status::ok();
sp<Client> client = nullptr;
ret = connectHelper<ICameraClient,Client>(cameraClient, id, api1CameraId, halVersion,
- clientPackageName, clientUid, USE_CALLING_PID, API_1, /*shimUpdateOnly*/ false,
- /*out*/client);
+ clientPackageName, std::unique_ptr<String16>(), clientUid, USE_CALLING_PID, API_1,
+ /*shimUpdateOnly*/ false, /*out*/client);
if(!ret.isOk()) {
logRejected(id, CameraThreadState::getCallingPid(), String8(clientPackageName),
@@ -1347,22 +1441,69 @@
return ret;
}
-bool CameraService::shouldRejectHiddenCameraConnection(const String8 & cameraId) {
- // If the thread serving this call is not a hwbinder thread and the caller
- // isn't the cameraserver itself, and the camera id being requested is to be
- // publically hidden, we should reject the connection.
- if (!hardware::IPCThreadState::self()->isServingCall() &&
- CameraThreadState::getCallingPid() != getpid() &&
- isPublicallyHiddenSecureCamera(cameraId)) {
+bool CameraService::shouldSkipStatusUpdates(SystemCameraKind systemCameraKind,
+ bool isVendorListener, int clientPid, int clientUid) {
+ // If the client is not a vendor client, don't add listener if
+ // a) the camera is a publicly hidden secure camera OR
+ // b) the camera is a system only camera and the client doesn't
+ // have android.permission.SYSTEM_CAMERA permissions.
+ if (!isVendorListener && (systemCameraKind == SystemCameraKind::HIDDEN_SECURE_CAMERA ||
+ (systemCameraKind == SystemCameraKind::SYSTEM_ONLY_CAMERA &&
+ !hasPermissionsForSystemCamera(clientPid, clientUid)))) {
return true;
}
return false;
}
+bool CameraService::shouldRejectSystemCameraConnection(const String8& cameraId) const {
+ // Rules for rejection:
+ // 1) If cameraserver tries to access this camera device, accept the
+ // connection.
+ // 2) The camera device is a publicly hidden secure camera device AND some
+ // component is trying to access it on a non-hwbinder thread (generally a non HAL client),
+ // reject it.
+ // 3) if the camera device is advertised by the camera HAL as SYSTEM_ONLY
+ // and the serving thread is a non hwbinder thread, the client must have
+ // android.permission.SYSTEM_CAMERA permissions to connect.
+
+ int cPid = CameraThreadState::getCallingPid();
+ int cUid = CameraThreadState::getCallingUid();
+ SystemCameraKind systemCameraKind = SystemCameraKind::PUBLIC;
+ if (getSystemCameraKind(cameraId, &systemCameraKind) != OK) {
+ ALOGE("%s: Invalid camera id %s, ", __FUNCTION__, cameraId.c_str());
+ return true;
+ }
+
+ // (1) Cameraserver trying to connect, accept.
+ if (CameraThreadState::getCallingPid() == getpid()) {
+ return false;
+ }
+ // (2)
+ if (!hardware::IPCThreadState::self()->isServingCall() &&
+ systemCameraKind == SystemCameraKind::HIDDEN_SECURE_CAMERA) {
+ ALOGW("Rejecting access to secure hidden camera %s", cameraId.c_str());
+ return true;
+ }
+ // (3) Here we only check for permissions if it is a system only camera device. This is since
+ // getCameraCharacteristics() allows for calls to succeed (albeit after hiding some
+ // characteristics) even if clients don't have android.permission.CAMERA. We do not want the
+ // same behavior for system camera devices.
+ if (!hardware::IPCThreadState::self()->isServingCall() &&
+ systemCameraKind == SystemCameraKind::SYSTEM_ONLY_CAMERA &&
+ !hasPermissionsForSystemCamera(cPid, cUid)) {
+ ALOGW("Rejecting access to system only camera %s, inadequete permissions",
+ cameraId.c_str());
+ return true;
+ }
+
+ return false;
+}
+
Status CameraService::connectDevice(
const sp<hardware::camera2::ICameraDeviceCallbacks>& cameraCb,
const String16& cameraId,
const String16& clientPackageName,
+ const std::unique_ptr<String16>& clientFeatureId,
int clientUid,
/*out*/
sp<hardware::camera2::ICameraDeviceUser>* device) {
@@ -1372,6 +1513,7 @@
String8 id = String8(cameraId);
sp<CameraDeviceClient> client = nullptr;
String16 clientPackageNameAdj = clientPackageName;
+
if (hardware::IPCThreadState::self()->isServingCall()) {
std::string vendorClient =
StringPrintf("vendor.client.pid<%d>", CameraThreadState::getCallingPid());
@@ -1379,7 +1521,7 @@
}
ret = connectHelper<hardware::camera2::ICameraDeviceCallbacks,CameraDeviceClient>(cameraCb, id,
/*api1CameraId*/-1,
- CAMERA_HAL_API_VERSION_UNSPECIFIED, clientPackageNameAdj,
+ CAMERA_HAL_API_VERSION_UNSPECIFIED, clientPackageNameAdj, clientFeatureId,
clientUid, USE_CALLING_PID, API_2, /*shimUpdateOnly*/ false, /*out*/client);
if(!ret.isOk()) {
@@ -1394,8 +1536,9 @@
template<class CALLBACK, class CLIENT>
Status CameraService::connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId,
- int api1CameraId, int halVersion, const String16& clientPackageName, int clientUid,
- int clientPid, apiLevel effectiveApiLevel, bool shimUpdateOnly,
+ int api1CameraId, int halVersion, const String16& clientPackageName,
+ const std::unique_ptr<String16>& clientFeatureId, int clientUid, int clientPid,
+ apiLevel effectiveApiLevel, bool shimUpdateOnly,
/*out*/sp<CLIENT>& device) {
binder::Status ret = binder::Status::ok();
@@ -1408,14 +1551,6 @@
(halVersion == -1) ? "default" : std::to_string(halVersion).c_str(),
static_cast<int>(effectiveApiLevel));
- if (shouldRejectHiddenCameraConnection(cameraId)) {
- ALOGW("Attempting to connect to system-only camera id %s, connection rejected",
- cameraId.c_str());
- return STATUS_ERROR_FMT(ERROR_DISCONNECTED,
- "No camera device with ID \"%s\" currently available",
- cameraId.string());
-
- }
sp<CLIENT> client = nullptr;
{
// Acquire mServiceLock and prevent other clients from connecting
@@ -1486,7 +1621,7 @@
}
sp<BasicClient> tmp = nullptr;
- if(!(ret = makeClient(this, cameraCb, clientPackageName,
+ if(!(ret = makeClient(this, cameraCb, clientPackageName, clientFeatureId,
cameraId, api1CameraId, facing,
clientPid, clientUid, getpid(),
halVersion, deviceVersion, effectiveApiLevel,
@@ -1691,8 +1826,7 @@
if (pid != selfPid) {
// Ensure we're being called by system_server, or similar process with
// permissions to notify the camera service about system events
- if (!checkCallingPermission(
- String16("android.permission.CAMERA_SEND_SYSTEM_EVENTS"))) {
+ if (!checkCallingPermission(sCameraSendSystemEventsPermission)) {
const int uid = CameraThreadState::getCallingUid();
ALOGE("Permission Denial: cannot send updates to camera service about system"
" events from pid=%d, uid=%d", pid, uid);
@@ -1727,7 +1861,7 @@
Mutex::Autolock lock(mStatusListenerLock);
for (const auto& it : mListenerList) {
- auto ret = it.second->getListener()->onCameraAccessPrioritiesChanged();
+ auto ret = it->getListener()->onCameraAccessPrioritiesChanged();
if (!ret.isOk()) {
ALOGE("%s: Failed to trigger permission callback: %d", __FUNCTION__,
ret.exceptionCode());
@@ -1743,8 +1877,7 @@
if (pid != selfPid) {
// Ensure we're being called by system_server, or similar process with
// permissions to notify the camera service about system events
- if (!checkCallingPermission(
- String16("android.permission.CAMERA_SEND_SYSTEM_EVENTS"))) {
+ if (!checkCallingPermission(sCameraSendSystemEventsPermission)) {
const int uid = CameraThreadState::getCallingUid();
ALOGE("Permission Denial: cannot send updates to camera service about device"
" state changes from pid=%d, uid=%d", pid, uid);
@@ -1798,20 +1931,23 @@
return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "Null listener given to addListener");
}
+ auto clientUid = CameraThreadState::getCallingUid();
+ auto clientPid = CameraThreadState::getCallingPid();
+
Mutex::Autolock lock(mServiceLock);
{
Mutex::Autolock lock(mStatusListenerLock);
for (const auto &it : mListenerList) {
- if (IInterface::asBinder(it.second->getListener()) == IInterface::asBinder(listener)) {
+ if (IInterface::asBinder(it->getListener()) == IInterface::asBinder(listener)) {
ALOGW("%s: Tried to add listener %p which was already subscribed",
__FUNCTION__, listener.get());
return STATUS_ERROR(ERROR_ALREADY_EXISTS, "Listener already registered");
}
}
- auto clientUid = CameraThreadState::getCallingUid();
- sp<ServiceListener> serviceListener = new ServiceListener(this, listener, clientUid);
+ sp<ServiceListener> serviceListener =
+ new ServiceListener(this, listener, clientUid, clientPid, isVendorListener);
auto ret = serviceListener->initialize();
if (ret != NO_ERROR) {
String8 msg = String8::format("Failed to initialize service listener: %s (%d)",
@@ -1819,7 +1955,10 @@
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, msg.string());
}
- mListenerList.emplace_back(isVendorListener, serviceListener);
+ // The listener still needs to be added to the list of listeners, regardless of what
+ // permissions the listener process has / whether it is a vendor listener. Since it might be
+ // eligible to listen to other camera ids.
+ mListenerList.emplace_back(serviceListener);
mUidPolicy->registerMonitorUid(clientUid);
}
@@ -1830,21 +1969,21 @@
cameraStatuses->emplace_back(i.first, mapToInterface(i.second->getStatus()));
}
}
-
// Remove the camera statuses that should be hidden from the client, we do
// this after collecting the states in order to avoid holding
- // mCameraStatesLock and mInterfaceLock (held in
- // isPublicallyHiddenSecureCamera()) at the same time.
+ // mCameraStatesLock and mInterfaceLock (held in getSystemCameraKind()) at
+ // the same time.
cameraStatuses->erase(std::remove_if(cameraStatuses->begin(), cameraStatuses->end(),
- [this, &isVendorListener](const hardware::CameraStatus& s) {
- bool ret = !isVendorListener && isPublicallyHiddenSecureCamera(s.cameraId);
- if (ret) {
- ALOGV("Cannot add public listener for hidden system-only %s for pid %d",
- s.cameraId.c_str(), CameraThreadState::getCallingPid());
+ [this, &isVendorListener, &clientPid, &clientUid](const hardware::CameraStatus& s) {
+ SystemCameraKind deviceKind = SystemCameraKind::PUBLIC;
+ if (getSystemCameraKind(s.cameraId, &deviceKind) != OK) {
+ ALOGE("%s: Invalid camera id %s, skipping status update",
+ __FUNCTION__, s.cameraId.c_str());
+ return true;
}
- return ret;
- }),
- cameraStatuses->end());
+ return shouldSkipStatusUpdates(deviceKind, isVendorListener, clientPid,
+ clientUid);}), cameraStatuses->end());
+
/*
* Immediately signal current torch status to this listener only
@@ -1876,9 +2015,9 @@
{
Mutex::Autolock lock(mStatusListenerLock);
for (auto it = mListenerList.begin(); it != mListenerList.end(); it++) {
- if (IInterface::asBinder(it->second->getListener()) == IInterface::asBinder(listener)) {
- mUidPolicy->unregisterMonitorUid(it->second->getListenerUid());
- IInterface::asBinder(listener)->unlinkToDeath(it->second);
+ if (IInterface::asBinder((*it)->getListener()) == IInterface::asBinder(listener)) {
+ mUidPolicy->unregisterMonitorUid((*it)->getListenerUid());
+ IInterface::asBinder(listener)->unlinkToDeath(*it);
mListenerList.erase(it);
return Status::ok();
}
@@ -1994,6 +2133,7 @@
mActiveClientManager.remove(i);
}
}
+ updateAudioRestrictionLocked();
}
bool CameraService::evictClientIdByRemote(const wp<IBinder>& remote) {
@@ -2323,13 +2463,14 @@
CameraService::Client::Client(const sp<CameraService>& cameraService,
const sp<ICameraClient>& cameraClient,
const String16& clientPackageName,
+ const std::unique_ptr<String16>& clientFeatureId,
const String8& cameraIdStr,
int api1CameraId, int cameraFacing,
int clientPid, uid_t clientUid,
int servicePid) :
CameraService::BasicClient(cameraService,
IInterface::asBinder(cameraClient),
- clientPackageName,
+ clientPackageName, clientFeatureId,
cameraIdStr, cameraFacing,
clientPid, clientUid,
servicePid),
@@ -2359,16 +2500,24 @@
CameraService::BasicClient::BasicClient(const sp<CameraService>& cameraService,
const sp<IBinder>& remoteCallback,
- const String16& clientPackageName,
+ const String16& clientPackageName, const std::unique_ptr<String16>& clientFeatureId,
const String8& cameraIdStr, int cameraFacing,
int clientPid, uid_t clientUid,
int servicePid):
mCameraIdStr(cameraIdStr), mCameraFacing(cameraFacing),
- mClientPackageName(clientPackageName), mClientPid(clientPid), mClientUid(clientUid),
+ mClientPackageName(clientPackageName),
+ mClientPid(clientPid), mClientUid(clientUid),
mServicePid(servicePid),
mDisconnected(false),
+ mAudioRestriction(hardware::camera2::ICameraDeviceUser::AUDIO_RESTRICTION_NONE),
mRemoteBinder(remoteCallback)
{
+ if (clientFeatureId) {
+ mClientFeatureId = std::unique_ptr<String16>(new String16(*clientFeatureId));
+ } else {
+ mClientFeatureId = std::unique_ptr<String16>();
+ }
+
if (sCameraService == nullptr) {
sCameraService = cameraService;
}
@@ -2470,6 +2619,35 @@
return level == API_2;
}
+status_t CameraService::BasicClient::setAudioRestriction(int32_t mode) {
+ {
+ Mutex::Autolock l(mAudioRestrictionLock);
+ mAudioRestriction = mode;
+ }
+ sCameraService->updateAudioRestriction();
+ return OK;
+}
+
+int32_t CameraService::BasicClient::getServiceAudioRestriction() const {
+ return sCameraService->updateAudioRestriction();
+}
+
+int32_t CameraService::BasicClient::getAudioRestriction() const {
+ Mutex::Autolock l(mAudioRestrictionLock);
+ return mAudioRestriction;
+}
+
+bool CameraService::BasicClient::isValidAudioRestriction(int32_t mode) {
+ switch (mode) {
+ case hardware::camera2::ICameraDeviceUser::AUDIO_RESTRICTION_NONE:
+ case hardware::camera2::ICameraDeviceUser::AUDIO_RESTRICTION_VIBRATION:
+ case hardware::camera2::ICameraDeviceUser::AUDIO_RESTRICTION_VIBRATION_SOUND:
+ return true;
+ default:
+ return false;
+ }
+}
+
status_t CameraService::BasicClient::startCameraOps() {
ATRACE_CALL();
@@ -2483,8 +2661,9 @@
int32_t res;
mAppOpsManager->startWatchingMode(AppOpsManager::OP_CAMERA,
mClientPackageName, mOpsCallback);
- res = mAppOpsManager->startOpNoThrow(AppOpsManager::OP_CAMERA,
- mClientUid, mClientPackageName, /*startIfModeDefault*/ false);
+ res = mAppOpsManager->startOpNoThrow(AppOpsManager::OP_CAMERA, mClientUid,
+ mClientPackageName, /*startIfModeDefault*/ false, mClientFeatureId,
+ String16("start camera ") + String16(mCameraIdStr));
if (res == AppOpsManager::MODE_ERRORED) {
ALOGI("Camera %s: Access for \"%s\" has been revoked",
@@ -2526,7 +2705,7 @@
// Notify app ops that the camera is available again
if (mAppOpsManager != nullptr) {
mAppOpsManager->finishOp(AppOpsManager::OP_CAMERA, mClientUid,
- mClientPackageName);
+ mClientPackageName, mClientFeatureId);
mOpsActive = false;
}
// This function is called when a client disconnects. This should
@@ -2597,6 +2776,20 @@
// ----------------------------------------------------------------------------
+sp<CameraService> CameraService::OfflineClient::sCameraService;
+
+status_t CameraService::OfflineClient::startCameraOps() {
+ // TODO
+ return OK;
+}
+
+status_t CameraService::OfflineClient::finishCameraOps() {
+ // TODO
+ return OK;
+}
+
+// ----------------------------------------------------------------------------
+
void CameraService::Client::notifyError(int32_t errorCode,
const CaptureResultExtras& resultExtras) {
(void) resultExtras;
@@ -2689,7 +2882,7 @@
}
void CameraService::UidPolicy::onUidStateChanged(uid_t uid, int32_t procState,
- int64_t /*procStateSeq*/) {
+ int64_t procStateSeq __unused, int32_t capability __unused) {
bool procStateChange = false;
{
Mutex::Autolock _l(mUidLock);
@@ -2903,9 +3096,9 @@
// ----------------------------------------------------------------------------
CameraService::CameraState::CameraState(const String8& id, int cost,
- const std::set<String8>& conflicting, bool isHidden) : mId(id),
+ const std::set<String8>& conflicting, SystemCameraKind systemCameraKind) : mId(id),
mStatus(StatusInternal::NOT_PRESENT), mCost(cost), mConflicting(conflicting),
- mIsPublicallyHiddenSecureCamera(isHidden) {}
+ mSystemCameraKind(systemCameraKind) {}
CameraService::CameraState::~CameraState() {}
@@ -2934,8 +3127,8 @@
return mId;
}
-bool CameraService::CameraState::isPublicallyHiddenSecureCamera() const {
- return mIsPublicallyHiddenSecureCamera;
+SystemCameraKind CameraService::CameraState::getSystemCameraKind() const {
+ return mSystemCameraKind;
}
// ----------------------------------------------------------------------------
@@ -3066,7 +3259,7 @@
status_t CameraService::dump(int fd, const Vector<String16>& args) {
ATRACE_CALL();
- if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
+ if (checkCallingPermission(sDumpPermission) == false) {
dprintf(fd, "Permission Denial: can't dump CameraService from pid=%d, uid=%d\n",
CameraThreadState::getCallingPid(),
CameraThreadState::getCallingUid());
@@ -3090,6 +3283,8 @@
dprintf(fd, "\n== Service global info: ==\n\n");
dprintf(fd, "Number of camera devices: %d\n", mNumberOfCameras);
dprintf(fd, "Number of normal camera devices: %zu\n", mNormalDeviceIds.size());
+ dprintf(fd, "Number of public camera devices visible to API1: %zu\n",
+ mNormalDeviceIdsWithoutSystemCamera.size());
for (size_t i = 0; i < mNormalDeviceIds.size(); i++) {
dprintf(fd, " Device %zu maps to \"%s\"\n", i, mNormalDeviceIds[i].c_str());
}
@@ -3273,7 +3468,13 @@
cameraId.string());
return;
}
- bool isHidden = isPublicallyHiddenSecureCamera(cameraId);
+
+ // Avoid calling getSystemCameraKind() with mStatusListenerLock held (b/141756275)
+ SystemCameraKind deviceKind = SystemCameraKind::PUBLIC;
+ if (getSystemCameraKind(cameraId, &deviceKind) != OK) {
+ ALOGE("%s: Invalid camera id %s, skipping", __FUNCTION__, cameraId.string());
+ return;
+ }
bool supportsHAL3 = false;
// supportsCameraApi also holds mInterfaceMutex, we can't call it in the
// HIDL onStatusChanged wrapper call (we'll hold mStatusListenerLock and
@@ -3288,7 +3489,7 @@
}
// Update the status for this camera state, then send the onStatusChangedCallbacks to each
// of the listeners with both the mStatusStatus and mStatusListenerLock held
- state->updateStatus(status, cameraId, rejectSourceStates, [this, &isHidden, &supportsHAL3]
+ state->updateStatus(status, cameraId, rejectSourceStates, [this, &deviceKind, &supportsHAL3]
(const String8& cameraId, StatusInternal status) {
if (status != StatusInternal::ENUMERATING) {
@@ -3310,12 +3511,15 @@
Mutex::Autolock lock(mStatusListenerLock);
for (auto& listener : mListenerList) {
- if (!listener.first && (isHidden || !supportsHAL3)) {
- ALOGV("Skipping camera discovery callback for system-only / HAL1 camera %s",
- cameraId.c_str());
+ bool isVendorListener = listener->isVendorListener();
+ if (shouldSkipStatusUpdates(deviceKind, isVendorListener,
+ listener->getListenerPid(), listener->getListenerUid()) ||
+ (isVendorListener && !supportsHAL3)) {
+ ALOGV("Skipping discovery callback for system-only camera/HAL1 device %s",
+ cameraId.c_str());
continue;
}
- listener.second->getListener()->onStatusChanged(mapToInterface(status),
+ listener->getListener()->onStatusChanged(mapToInterface(status),
String16(cameraId));
}
});
@@ -3515,4 +3719,25 @@
" help print this message\n");
}
+int32_t CameraService::updateAudioRestriction() {
+ Mutex::Autolock lock(mServiceLock);
+ return updateAudioRestrictionLocked();
+}
+
+int32_t CameraService::updateAudioRestrictionLocked() {
+ int32_t mode = 0;
+ // iterate through all active client
+ for (const auto& i : mActiveClientManager.getAll()) {
+ const auto clientSp = i->getValue();
+ mode |= clientSp->getAudioRestriction();
+ }
+
+ bool modeChanged = (mAudioRestriction != mode);
+ mAudioRestriction = mode;
+ if (modeChanged) {
+ mAppOps.setCameraAudioRestriction(mode);
+ }
+ return mode;
+}
+
}; // namespace android
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 8bb78cd..dae9c09 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -71,6 +71,7 @@
public:
class Client;
class BasicClient;
+ class OfflineClient;
// The effective API level. The Camera2 API running in LEGACY mode counts as API_1.
enum apiLevel {
@@ -134,7 +135,8 @@
virtual binder::Status connectDevice(
const sp<hardware::camera2::ICameraDeviceCallbacks>& cameraCb, const String16& cameraId,
- const String16& clientPackageName, int32_t clientUid,
+ const String16& clientPackageName, const std::unique_ptr<String16>& clientFeatureId,
+ int32_t clientUid,
/*out*/
sp<hardware::camera2::ICameraDeviceUser>* device);
@@ -258,10 +260,24 @@
// Block the client form using the camera
virtual void block();
+
+ // set audio restriction from client
+ // Will call into camera service and hold mServiceLock
+ virtual status_t setAudioRestriction(int32_t mode);
+
+ // Get current global audio restriction setting
+ // Will call into camera service and hold mServiceLock
+ virtual int32_t getServiceAudioRestriction() const;
+
+ // Get current audio restriction setting for this client
+ virtual int32_t getAudioRestriction() const;
+
+ static bool isValidAudioRestriction(int32_t mode);
protected:
BasicClient(const sp<CameraService>& cameraService,
const sp<IBinder>& remoteCallback,
const String16& clientPackageName,
+ const std::unique_ptr<String16>& clientFeatureId,
const String8& cameraIdStr,
int cameraFacing,
int clientPid,
@@ -281,11 +297,15 @@
const String8 mCameraIdStr;
const int mCameraFacing;
String16 mClientPackageName;
+ std::unique_ptr<String16> mClientFeatureId;
pid_t mClientPid;
const uid_t mClientUid;
const pid_t mServicePid;
bool mDisconnected;
+ mutable Mutex mAudioRestrictionLock;
+ int32_t mAudioRestriction;
+
// - The app-side Binder interface to receive callbacks from us
sp<IBinder> mRemoteBinder; // immutable after constructor
@@ -349,6 +369,7 @@
Client(const sp<CameraService>& cameraService,
const sp<hardware::ICameraClient>& cameraClient,
const String16& clientPackageName,
+ const std::unique_ptr<String16>& clientFeatureId,
const String8& cameraIdStr,
int api1CameraId,
int cameraFacing,
@@ -381,6 +402,87 @@
int mCameraId; // All API1 clients use integer camera IDs
}; // class Client
+
+ // 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 OfflineClient : public virtual RefBase {
+
+ virtual status_t dump(int fd, const Vector<String16>& args) = 0;
+
+ // Block the client form using the camera
+ virtual void block() = 0;
+
+ // Return the package name for this client
+ virtual String16 getPackageName() const = 0;
+
+ // Notify client about a fatal error
+ // TODO: maybe let impl notify within block?
+ virtual void notifyError(int32_t errorCode,
+ const CaptureResultExtras& resultExtras) = 0;
+
+ // Get the UID of the application client using this
+ virtual uid_t getClientUid() const = 0;
+
+ // Get the PID of the application client using this
+ virtual int getClientPid() const = 0;
+
+ protected:
+ OfflineClient(const sp<CameraService>& cameraService,
+ const String16& clientPackageName,
+ const String8& cameraIdStr,
+ int clientPid,
+ uid_t clientUid,
+ int servicePid): mCameraIdStr(cameraIdStr),
+ mClientPackageName(clientPackageName), mClientPid(clientPid),
+ mClientUid(clientUid), mServicePid(servicePid) {
+ if (sCameraService == nullptr) {
+ sCameraService = cameraService;
+ }
+ }
+
+ virtual ~OfflineClient() { /*TODO*/ }
+
+ // these are initialized in the constructor.
+ static sp<CameraService> sCameraService;
+ const String8 mCameraIdStr;
+ String16 mClientPackageName;
+ pid_t mClientPid;
+ const uid_t mClientUid;
+ const pid_t mServicePid;
+ bool mDisconnected;
+
+ // - The app-side Binder interface to receive callbacks from us
+ sp<IBinder> mRemoteBinder; // immutable after constructor
+
+ // permissions management
+ status_t startCameraOps();
+ status_t finishCameraOps();
+
+ private:
+ std::unique_ptr<AppOpsManager> mAppOpsManager = nullptr;
+
+ class OpsCallback : public BnAppOpsCallback {
+ public:
+ explicit OpsCallback(wp<OfflineClient> client) : mClient(client) {}
+ virtual void opChanged(int32_t /*op*/, const String16& /*packageName*/) {
+ //TODO
+ }
+
+ private:
+ wp<OfflineClient> mClient;
+
+ }; // class OpsCallback
+
+ sp<OpsCallback> mOpsCallback;
+
+ // IAppOpsCallback interface, indirected through opListener
+ // virtual void opChanged(int32_t op, const String16& packageName);
+ }; // class OfflineClient
+
/**
* A listener class that implements the LISTENER interface for use with a ClientManager, and
* implements the following methods:
@@ -439,6 +541,9 @@
}; // class CameraClientManager
+ int32_t updateAudioRestriction();
+ int32_t updateAudioRestrictionLocked();
+
private:
typedef hardware::camera::common::V1_0::CameraDeviceStatus CameraDeviceStatus;
@@ -472,7 +577,7 @@
* returned in the HAL's camera_info struct for each device.
*/
CameraState(const String8& id, int cost, const std::set<String8>& conflicting,
- bool isHidden);
+ SystemCameraKind deviceKind);
virtual ~CameraState();
/**
@@ -525,9 +630,9 @@
String8 getId() const;
/**
- * Return if the camera device is a publically hidden secure camera
+ * Return the kind (SystemCameraKind) of this camera device.
*/
- bool isPublicallyHiddenSecureCamera() const;
+ SystemCameraKind getSystemCameraKind() const;
private:
const String8 mId;
@@ -536,7 +641,7 @@
std::set<String8> mConflicting;
mutable Mutex mStatusLock;
CameraParameters mShimParams;
- const bool mIsPublicallyHiddenSecureCamera;
+ const SystemCameraKind mSystemCameraKind;
}; // class CameraState
// Observer for UID lifecycle enforcing that UIDs in idle
@@ -555,7 +660,8 @@
void onUidGone(uid_t uid, bool disabled);
void onUidActive(uid_t uid);
void onUidIdle(uid_t uid, bool disabled);
- void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq);
+ void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq,
+ int32_t capability);
void addOverrideUid(uid_t uid, String16 callingPackage, bool active);
void removeOverrideUid(uid_t uid, String16 callingPackage);
@@ -640,18 +746,36 @@
sp<BasicClient>* client,
std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>>* partial);
- // Should an operation attempt on a cameraId be rejected, if the camera id is
- // advertised as a publically hidden secure camera, by the camera HAL ?
- bool shouldRejectHiddenCameraConnection(const String8& cameraId);
+ // Should an operation attempt on a cameraId be rejected ? (this can happen
+ // under various conditions. For example if a camera device is advertised as
+ // system only or hidden secure camera, amongst possible others.
+ bool shouldRejectSystemCameraConnection(const String8 & cameraId) const;
- bool isPublicallyHiddenSecureCamera(const String8& cameraId);
+ // Should a device status update be skipped for a particular camera device ? (this can happen
+ // under various conditions. For example if a camera device is advertised as
+ // system only or hidden secure camera, amongst possible others.
+ static bool shouldSkipStatusUpdates(SystemCameraKind systemCameraKind, bool isVendorListener,
+ int clientPid, int clientUid);
+
+ // Gets the kind of camera device (i.e public, hidden secure or system only)
+ // getSystemCameraKind() needs mInterfaceMutex which might lead to deadlocks
+ // if held along with mStatusListenerLock (depending on lock ordering, b/141756275), it is
+ // recommended that we don't call this function with mStatusListenerLock held.
+ status_t getSystemCameraKind(const String8& cameraId, SystemCameraKind *kind) const;
+
+ // Update the set of API1Compatible camera devices without including system
+ // cameras and secure cameras. This is used for hiding system only cameras
+ // from clients using camera1 api and not having android.permission.SYSTEM_CAMERA.
+ // This function expects @param normalDeviceIds, to have normalDeviceIds
+ // sorted in alpha-numeric order.
+ void filterAPI1SystemCameraLocked(const std::vector<std::string> &normalDeviceIds);
// Single implementation shared between the various connect calls
template<class CALLBACK, class CLIENT>
binder::Status connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId,
int api1CameraId, int halVersion, const String16& clientPackageName,
- int clientUid, int clientPid, apiLevel effectiveApiLevel, bool shimUpdateOnly,
- /*out*/sp<CLIENT>& device);
+ const std::unique_ptr<String16>& clientFeatureId, int clientUid, int clientPid,
+ apiLevel effectiveApiLevel, bool shimUpdateOnly, /*out*/sp<CLIENT>& device);
// Lock guarding camera service state
Mutex mServiceLock;
@@ -800,9 +924,14 @@
*/
void updateCameraNumAndIds();
+ // Number of camera devices (excluding hidden secure cameras)
int mNumberOfCameras;
+ // Number of camera devices (excluding hidden secure cameras and
+ // system cameras)
+ int mNumberOfCamerasWithoutSystemCamera;
std::vector<std::string> mNormalDeviceIds;
+ std::vector<std::string> mNormalDeviceIdsWithoutSystemCamera;
// sounds
sp<MediaPlayer> newMediaPlayer(const char *file);
@@ -819,7 +948,9 @@
class ServiceListener : public virtual IBinder::DeathRecipient {
public:
ServiceListener(sp<CameraService> parent, sp<hardware::ICameraServiceListener> listener,
- int uid) : mParent(parent), mListener(listener), mListenerUid(uid) {}
+ int uid, int pid, bool isVendorClient)
+ : mParent(parent), mListener(listener), mListenerUid(uid), mListenerPid(pid),
+ mIsVendorListener(isVendorClient) { }
status_t initialize() {
return IInterface::asBinder(mListener)->linkToDeath(this);
@@ -833,16 +964,20 @@
}
int getListenerUid() { return mListenerUid; }
+ int getListenerPid() { return mListenerPid; }
sp<hardware::ICameraServiceListener> getListener() { return mListener; }
+ bool isVendorListener() { return mIsVendorListener; }
private:
wp<CameraService> mParent;
sp<hardware::ICameraServiceListener> mListener;
- int mListenerUid;
+ int mListenerUid = -1;
+ int mListenerPid = -1;
+ bool mIsVendorListener = false;
};
// Guarded by mStatusListenerMutex
- std::vector<std::pair<bool, sp<ServiceListener>>> mListenerList;
+ std::vector<sp<ServiceListener>> mListenerList;
Mutex mStatusListenerLock;
@@ -936,9 +1071,10 @@
static String8 getFormattedCurrentTime();
static binder::Status makeClient(const sp<CameraService>& cameraService,
- const sp<IInterface>& cameraCb, const String16& packageName, const String8& cameraId,
- int api1CameraId, int facing, int clientPid, uid_t clientUid, int servicePid,
- int halVersion, int deviceVersion, apiLevel effectiveApiLevel,
+ const sp<IInterface>& cameraCb, const String16& packageName,
+ const std::unique_ptr<String16>& featureId, const String8& cameraId, int api1CameraId,
+ int facing, int clientPid, uid_t clientUid, int servicePid, int halVersion,
+ int deviceVersion, apiLevel effectiveApiLevel,
/*out*/sp<BasicClient>* client);
status_t checkCameraAccess(const String16& opPackageName);
@@ -958,6 +1094,13 @@
void broadcastTorchModeStatus(const String8& cameraId,
hardware::camera::common::V1_0::TorchModeStatus status);
+
+ // 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;
};
} // namespace android
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 162b50f..5dbbc0b 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -50,13 +50,14 @@
Camera2Client::Camera2Client(const sp<CameraService>& cameraService,
const sp<hardware::ICameraClient>& cameraClient,
const String16& clientPackageName,
+ const std::unique_ptr<String16>& clientFeatureId,
const String8& cameraDeviceId,
int api1CameraId,
int cameraFacing,
int clientPid,
uid_t clientUid,
int servicePid):
- Camera2ClientBase(cameraService, cameraClient, clientPackageName,
+ Camera2ClientBase(cameraService, cameraClient, clientPackageName, clientFeatureId,
cameraDeviceId, api1CameraId, cameraFacing,
clientPid, clientUid, servicePid),
mParameters(api1CameraId, cameraFacing)
@@ -959,6 +960,11 @@
case Parameters::RECORD:
case Parameters::PREVIEW:
syncWithDevice();
+ // Due to flush a camera device sync is not a sufficient
+ // guarantee that the current client parameters are
+ // correctly applied. To resolve this wait for the current
+ // request id to return in the results.
+ waitUntilCurrentRequestIdLocked();
res = stopStream();
if (res != OK) {
ALOGE("%s: Camera %d: Can't stop streaming: %s (%d)",
@@ -2253,6 +2259,58 @@
return OK;
}
+status_t Camera2Client::setAudioRestriction(int /*mode*/) {
+ // Empty implementation. setAudioRestriction is hidden interface and not
+ // supported by android.hardware.Camera API
+ return INVALID_OPERATION;
+}
+
+int32_t Camera2Client::getGlobalAudioRestriction() {
+ // Empty implementation. getAudioRestriction is hidden interface and not
+ // supported by android.hardware.Camera API
+ return INVALID_OPERATION;
+}
+
+status_t Camera2Client::waitUntilCurrentRequestIdLocked() {
+ int32_t activeRequestId = mStreamingProcessor->getActiveRequestId();
+ if (activeRequestId != 0) {
+ auto res = waitUntilRequestIdApplied(activeRequestId,
+ mDevice->getExpectedInFlightDuration());
+ if (res == TIMED_OUT) {
+ ALOGE("%s: Camera %d: Timed out waiting for current request id to return in results!",
+ __FUNCTION__, mCameraId);
+ return res;
+ } else if (res != OK) {
+ ALOGE("%s: Camera %d: Error while waiting for current request id to return in results!",
+ __FUNCTION__, mCameraId);
+ return res;
+ }
+ }
+
+ return OK;
+}
+
+status_t Camera2Client::waitUntilRequestIdApplied(int32_t requestId, nsecs_t timeout) {
+ Mutex::Autolock l(mLatestRequestMutex);
+ while (mLatestRequestId != requestId) {
+ nsecs_t startTime = systemTime();
+
+ auto res = mLatestRequestSignal.waitRelative(mLatestRequestMutex, timeout);
+ if (res != OK) return res;
+
+ timeout -= (systemTime() - startTime);
+ }
+
+ return OK;
+}
+
+void Camera2Client::notifyRequestId(int32_t requestId) {
+ Mutex::Autolock al(mLatestRequestMutex);
+
+ mLatestRequestId = requestId;
+ mLatestRequestSignal.signal();
+}
+
const char* Camera2Client::kAutofocusLabel = "autofocus";
const char* Camera2Client::kTakepictureLabel = "take_picture";
diff --git a/services/camera/libcameraservice/api1/Camera2Client.h b/services/camera/libcameraservice/api1/Camera2Client.h
index a9ea271..8034ab4 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.h
+++ b/services/camera/libcameraservice/api1/Camera2Client.h
@@ -83,6 +83,8 @@
virtual void notifyError(int32_t errorCode,
const CaptureResultExtras& resultExtras);
virtual status_t setVideoTarget(const sp<IGraphicBufferProducer>& bufferProducer);
+ virtual status_t setAudioRestriction(int mode);
+ virtual int32_t getGlobalAudioRestriction();
/**
* Interface used by CameraService
@@ -91,6 +93,7 @@
Camera2Client(const sp<CameraService>& cameraService,
const sp<hardware::ICameraClient>& cameraClient,
const String16& clientPackageName,
+ const std::unique_ptr<String16>& clientFeatureId,
const String8& cameraDeviceId,
int api1CameraId,
int cameraFacing,
@@ -122,6 +125,8 @@
camera2::SharedParameters& getParameters();
+ void notifyRequestId(int32_t requestId);
+
int getPreviewStreamId() const;
int getCaptureStreamId() const;
int getCallbackStreamId() const;
@@ -227,6 +232,12 @@
status_t initializeImpl(TProviderPtr providerPtr, const String8& monitorTags);
bool isZslEnabledInStillTemplate();
+
+ mutable Mutex mLatestRequestMutex;
+ Condition mLatestRequestSignal;
+ int32_t mLatestRequestId = -1;
+ status_t waitUntilRequestIdApplied(int32_t requestId, nsecs_t timeout);
+ status_t waitUntilCurrentRequestIdLocked();
};
}; // namespace android
diff --git a/services/camera/libcameraservice/api1/CameraClient.cpp b/services/camera/libcameraservice/api1/CameraClient.cpp
index d65ac7b..83da880 100644
--- a/services/camera/libcameraservice/api1/CameraClient.cpp
+++ b/services/camera/libcameraservice/api1/CameraClient.cpp
@@ -34,11 +34,11 @@
CameraClient::CameraClient(const sp<CameraService>& cameraService,
const sp<hardware::ICameraClient>& cameraClient,
- const String16& clientPackageName,
+ const String16& clientPackageName, const std::unique_ptr<String16>& clientFeatureId,
int cameraId, int cameraFacing,
int clientPid, int clientUid,
int servicePid):
- Client(cameraService, cameraClient, clientPackageName,
+ Client(cameraService, cameraClient, clientPackageName, clientFeatureId,
String8::format("%d", cameraId), cameraId, cameraFacing, clientPid,
clientUid, servicePid)
{
@@ -534,7 +534,7 @@
}
if (mHardware != nullptr) {
- VideoNativeHandleMetadata *metadata = (VideoNativeHandleMetadata*)(dataPtr->pointer());
+ VideoNativeHandleMetadata *metadata = (VideoNativeHandleMetadata*)(dataPtr->unsecurePointer());
metadata->eType = kMetadataBufferTypeNativeHandleSource;
metadata->pHandle = handle;
mHardware->releaseRecordingFrame(dataPtr);
@@ -573,7 +573,7 @@
}
if (!disconnected) {
- VideoNativeHandleMetadata *metadata = (VideoNativeHandleMetadata*)(dataPtr->pointer());
+ VideoNativeHandleMetadata *metadata = (VideoNativeHandleMetadata*)(dataPtr->unsecurePointer());
metadata->eType = kMetadataBufferTypeNativeHandleSource;
metadata->pHandle = handle;
frames.push_back(dataPtr);
@@ -916,8 +916,12 @@
ALOGE("%s: dataPtr does not contain VideoNativeHandleMetadata!", __FUNCTION__);
return;
}
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
VideoNativeHandleMetadata *metadata =
- (VideoNativeHandleMetadata*)(msg.dataPtr->pointer());
+ (VideoNativeHandleMetadata*)(msg.dataPtr->unsecurePointer());
if (metadata->eType == kMetadataBufferTypeNativeHandleSource) {
handle = metadata->pHandle;
}
@@ -1073,8 +1077,12 @@
// Check if dataPtr contains a VideoNativeHandleMetadata.
if (dataPtr->size() == sizeof(VideoNativeHandleMetadata)) {
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
VideoNativeHandleMetadata *metadata =
- (VideoNativeHandleMetadata*)(dataPtr->pointer());
+ (VideoNativeHandleMetadata*)(dataPtr->unsecurePointer());
if (metadata->eType == kMetadataBufferTypeNativeHandleSource) {
handle = metadata->pHandle;
}
@@ -1171,4 +1179,25 @@
return INVALID_OPERATION;
}
+status_t CameraClient::setAudioRestriction(int mode) {
+ if (!isValidAudioRestriction(mode)) {
+ ALOGE("%s: invalid audio restriction mode %d", __FUNCTION__, mode);
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mLock);
+ if (checkPidAndHardware() != NO_ERROR) {
+ return INVALID_OPERATION;
+ }
+ return BasicClient::setAudioRestriction(mode);
+}
+
+int32_t CameraClient::getGlobalAudioRestriction() {
+ Mutex::Autolock lock(mLock);
+ if (checkPidAndHardware() != NO_ERROR) {
+ return INVALID_OPERATION;
+ }
+ return BasicClient::getServiceAudioRestriction();
+}
+
}; // namespace android
diff --git a/services/camera/libcameraservice/api1/CameraClient.h b/services/camera/libcameraservice/api1/CameraClient.h
index 9530b6c..501ad18 100644
--- a/services/camera/libcameraservice/api1/CameraClient.h
+++ b/services/camera/libcameraservice/api1/CameraClient.h
@@ -59,11 +59,14 @@
virtual String8 getParameters() const;
virtual status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2);
virtual status_t setVideoTarget(const sp<IGraphicBufferProducer>& bufferProducer);
+ virtual status_t setAudioRestriction(int mode);
+ virtual int32_t getGlobalAudioRestriction();
// Interface used by CameraService
CameraClient(const sp<CameraService>& cameraService,
const sp<hardware::ICameraClient>& cameraClient,
const String16& clientPackageName,
+ const std::unique_ptr<String16>& clientFeatureId,
int cameraId,
int cameraFacing,
int clientPid,
diff --git a/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp b/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
index 88799f9..0c01a91 100644
--- a/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
+++ b/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
@@ -265,7 +265,7 @@
Mutex::Autolock l(mInputMutex);
while (!mStartCapture) {
res = mStartCaptureSignal.waitRelative(mInputMutex,
- kWaitDuration);
+ kIdleWaitDuration);
if (res == TIMED_OUT) break;
}
if (mStartCapture) {
diff --git a/services/camera/libcameraservice/api1/client2/CaptureSequencer.h b/services/camera/libcameraservice/api1/client2/CaptureSequencer.h
index 727dd53..9475a39 100644
--- a/services/camera/libcameraservice/api1/client2/CaptureSequencer.h
+++ b/services/camera/libcameraservice/api1/client2/CaptureSequencer.h
@@ -111,6 +111,7 @@
* Internal to CaptureSequencer
*/
static const nsecs_t kWaitDuration = 100000000; // 100 ms
+ static const nsecs_t kIdleWaitDuration = 10000000; // 10 ms
static const int kMaxTimeoutsForPrecaptureStart = 10; // 1 sec
static const int kMaxTimeoutsForPrecaptureEnd = 20; // 2 sec
static const int kMaxTimeoutsForCaptureEnd = 40; // 4 sec
diff --git a/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp b/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp
index 683e84d..63e293a 100644
--- a/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp
@@ -86,6 +86,12 @@
process3aState(frame, client);
}
+ if (mCurrentRequestId != frame.mResultExtras.requestId) {
+ mCurrentRequestId = frame.mResultExtras.requestId;
+
+ client->notifyRequestId(mCurrentRequestId);
+ }
+
return FrameProcessorBase::processSingleFrame(frame, device);
}
diff --git a/services/camera/libcameraservice/api1/client2/FrameProcessor.h b/services/camera/libcameraservice/api1/client2/FrameProcessor.h
index 8183c12..142b8cd 100644
--- a/services/camera/libcameraservice/api1/client2/FrameProcessor.h
+++ b/services/camera/libcameraservice/api1/client2/FrameProcessor.h
@@ -94,6 +94,7 @@
};
AlgState m3aState;
+ int32_t mCurrentRequestId = -1;
// frame number -> pending 3A states that not all data are received yet.
KeyedVector<int32_t, AlgState> mPending3AStates;
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 3587db3..28421ba 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -54,6 +54,7 @@
const sp<CameraService>& cameraService,
const sp<hardware::camera2::ICameraDeviceCallbacks>& remoteCallback,
const String16& clientPackageName,
+ const std::unique_ptr<String16>& clientFeatureId,
const String8& cameraId,
int api1CameraId,
int cameraFacing,
@@ -63,6 +64,7 @@
BasicClient(cameraService,
IInterface::asBinder(remoteCallback),
clientPackageName,
+ clientFeatureId,
cameraId,
cameraFacing,
clientPid,
@@ -78,12 +80,13 @@
CameraDeviceClient::CameraDeviceClient(const sp<CameraService>& cameraService,
const sp<hardware::camera2::ICameraDeviceCallbacks>& remoteCallback,
const String16& clientPackageName,
+ const std::unique_ptr<String16>& clientFeatureId,
const String8& cameraId,
int cameraFacing,
int clientPid,
uid_t clientUid,
int servicePid) :
- Camera2ClientBase(cameraService, remoteCallback, clientPackageName,
+ Camera2ClientBase(cameraService, remoteCallback, clientPackageName, clientFeatureId,
cameraId, /*API1 camera ID*/ -1,
cameraFacing, clientPid, clientUid, servicePid),
mInputStream(),
@@ -1870,6 +1873,108 @@
return res;
}
+binder::Status CameraDeviceClient::setCameraAudioRestriction(int32_t mode) {
+ ATRACE_CALL();
+ binder::Status res;
+ if (!(res = checkPidStatus(__FUNCTION__)).isOk()) return res;
+
+ if (!isValidAudioRestriction(mode)) {
+ String8 msg = String8::format("Camera %s: invalid audio restriction mode %d",
+ mCameraIdStr.string(), mode);
+ ALOGW("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+ }
+
+ Mutex::Autolock icl(mBinderSerializationLock);
+ BasicClient::setAudioRestriction(mode);
+ return binder::Status::ok();
+}
+
+binder::Status CameraDeviceClient::getGlobalAudioRestriction(/*out*/ int32_t* outMode) {
+ ATRACE_CALL();
+ binder::Status res;
+ if (!(res = checkPidStatus(__FUNCTION__)).isOk()) return res;
+ Mutex::Autolock icl(mBinderSerializationLock);
+ if (outMode != nullptr) {
+ *outMode = BasicClient::getServiceAudioRestriction();
+ }
+ return binder::Status::ok();
+}
+
+binder::Status CameraDeviceClient::switchToOffline(
+ const sp<hardware::camera2::ICameraDeviceCallbacks>& cameraCb,
+ const std::vector<view::Surface>& offlineOutputs,
+ /*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 (offlineOutputs.empty()) {
+ String8 msg = String8::format("Offline outputs 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(offlineOutputs.size());
+ for (auto& surface : offlineOutputs) {
+ sp<IBinder> binder = IInterface::asBinder(surface.graphicBufferProducer);
+ ssize_t index = mStreamMap.indexOfKey(binder);
+ if (index == NAME_NOT_FOUND) {
+ String8 msg = String8::format("Offline output is invalid");
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+ }
+ // TODO: Also check whether the offline output is supported by Hal for offline mode.
+
+ sp<Surface> s = new Surface(surface.graphicBufferProducer);
+ bool isCompositeStream = camera3::DepthCompositeStream::isDepthCompositeStream(s);
+ isCompositeStream |= camera3::HeicCompositeStream::isHeicCompositeStream(s);
+ if (isCompositeStream) {
+ // TODO: Add composite specific handling
+ } else {
+ offlineStreamIds.push_back(mStreamMap.valueAt(index).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 = new CameraOfflineSessionClient(sCameraService,
+ offlineSession, cameraCb, mClientPackageName, mCameraIdStr, mClientPid, mClientUid,
+ mServicePid);
+ ret = offlineClient->initialize();
+ if (ret == OK) {
+ // TODO: We need to update mStreamMap, mConfiguredOutputs
+ } else {
+ return STATUS_ERROR_FMT(CameraService::ERROR_ILLEGAL_ARGUMENT,
+ "Camera %s: Failed to initilize offline session: %s (%d)",
+ mCameraIdStr.string(), strerror(ret), ret);
+ }
+
+ *session = offlineClient;
+
+ return binder::Status::ok();
+}
+
status_t CameraDeviceClient::dump(int fd, const Vector<String16>& args) {
return BasicClient::dump(fd, args);
}
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index 1c5abb0..0a8f377 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"
@@ -47,6 +48,7 @@
CameraDeviceClientBase(const sp<CameraService>& cameraService,
const sp<hardware::camera2::ICameraDeviceCallbacks>& remoteCallback,
const String16& clientPackageName,
+ const std::unique_ptr<String16>& clientFeatureId,
const String8& cameraId,
int api1CameraId,
int cameraFacing,
@@ -152,6 +154,16 @@
virtual binder::Status finalizeOutputConfigurations(int32_t streamId,
const hardware::camera2::params::OutputConfiguration &outputConfiguration) override;
+ virtual binder::Status setCameraAudioRestriction(int32_t mode) override;
+
+ virtual binder::Status getGlobalAudioRestriction(/*out*/int32_t* outMode) override;
+
+ virtual binder::Status switchToOffline(
+ const sp<hardware::camera2::ICameraDeviceCallbacks>& cameraCb,
+ const std::vector<view::Surface>& offlineOutputs,
+ /*out*/
+ sp<hardware::camera2::ICameraOfflineSession>* session) override;
+
/**
* Interface used by CameraService
*/
@@ -159,6 +171,7 @@
CameraDeviceClient(const sp<CameraService>& cameraService,
const sp<hardware::camera2::ICameraDeviceCallbacks>& remoteCallback,
const String16& clientPackageName,
+ const std::unique_ptr<String16>& clientFeatureId,
const String8& cameraId,
int cameraFacing,
int clientPid,
diff --git a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h
new file mode 100644
index 0000000..cb83e29
--- /dev/null
+++ b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h
@@ -0,0 +1,85 @@
+/*
+ * 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 "CameraService.h"
+
+namespace android {
+
+using android::hardware::camera2::ICameraDeviceCallbacks;
+
+class CameraOfflineSessionClient :
+ public CameraService::OfflineClient,
+ public hardware::camera2::BnCameraOfflineSession
+ // public camera2::FrameProcessorBase::FilteredListener?
+{
+public:
+ CameraOfflineSessionClient(
+ const sp<CameraService>& cameraService,
+ sp<CameraOfflineSessionBase> session,
+ const sp<ICameraDeviceCallbacks>& remoteCallback,
+ const String16& clientPackageName,
+ const String8& cameraIdStr,
+ int clientPid, uid_t clientUid, int servicePid) :
+ CameraService::OfflineClient(cameraService, clientPackageName,
+ cameraIdStr, clientPid, clientUid, servicePid),
+ mRemoteCallback(remoteCallback), mOfflineSession(session) {}
+
+ ~CameraOfflineSessionClient() {}
+
+ virtual binder::Status disconnect() override { return binder::Status::ok(); }
+
+ virtual status_t dump(int /*fd*/, const Vector<String16>& /*args*/) override {
+ return OK;
+ }
+
+ // Block the client form using the camera
+ virtual void block() override {};
+
+ // Return the package name for this client
+ virtual String16 getPackageName() const override { String16 ret; return ret; };
+
+ // Notify client about a fatal error
+ // TODO: maybe let impl notify within block?
+ virtual void notifyError(int32_t /*errorCode*/,
+ const CaptureResultExtras& /*resultExtras*/) override {}
+
+ // Get the UID of the application client using this
+ virtual uid_t getClientUid() const override { return 0; }
+
+ // Get the PID of the application client using this
+ virtual int getClientPid() const override { return 0; }
+
+ status_t initialize() {
+ // TODO: Talk to camera service to add the offline session client book keeping
+ return OK;
+ }
+private:
+ sp<CameraOfflineSessionBase> mSession;
+
+ sp<hardware::camera2::ICameraDeviceCallbacks> mRemoteCallback;
+ // This class is responsible to convert HAL callbacks to AIDL callbacks
+
+ sp<CameraOfflineSessionBase> mOfflineSession;
+};
+
+} // namespace android
+
+#endif // ANDROID_SERVERS_CAMERA_PHOTOGRAPHY_CAMERAOFFLINESESSIONCLIENT_H
diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
index 0b91016..ac6f8d6 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;
diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.h b/services/camera/libcameraservice/api2/DepthCompositeStream.h
index 28a7826..60c6b1e 100644
--- a/services/camera/libcameraservice/api2/DepthCompositeStream.h
+++ b/services/camera/libcameraservice/api2/DepthCompositeStream.h
@@ -126,8 +126,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 9790e32..9cdad70 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) {
}
@@ -525,6 +526,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 +858,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 +1144,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,
@@ -1260,7 +1270,7 @@
outputFormat->setInt32(KEY_I_FRAME_INTERVAL, 0);
outputFormat->setInt32(KEY_COLOR_FORMAT,
useGrid ? COLOR_FormatYUV420Flexible : COLOR_FormatSurface);
- outputFormat->setInt32(KEY_FRAME_RATE, gridRows * gridCols);
+ outputFormat->setInt32(KEY_FRAME_RATE, useGrid ? gridRows * gridCols : kNoGridOpRate);
// This only serves as a hint to encoder when encoding is not real-time.
outputFormat->setInt32(KEY_OPERATING_RATE, useGrid ? kGridOpRate : kNoGridOpRate);
@@ -1546,6 +1556,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..e88471d 100644
--- a/services/camera/libcameraservice/api2/HeicCompositeStream.h
+++ b/services/camera/libcameraservice/api2/HeicCompositeStream.h
@@ -199,6 +199,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 +241,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 78feb3e..8792f9a 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -44,13 +44,14 @@
const sp<CameraService>& cameraService,
const sp<TCamCallbacks>& remoteCallback,
const String16& clientPackageName,
+ const std::unique_ptr<String16>& clientFeatureId,
const String8& cameraId,
int api1CameraId,
int cameraFacing,
int clientPid,
uid_t clientUid,
int servicePid):
- TClientBase(cameraService, remoteCallback, clientPackageName,
+ TClientBase(cameraService, remoteCallback, clientPackageName, clientFeatureId,
cameraId, api1CameraId, cameraFacing, clientPid, clientUid, servicePid),
mSharedCameraCallbacks(remoteCallback),
mDeviceVersion(cameraService->getDeviceVersion(TClientBase::mCameraIdStr)),
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h
index 6693847..12cba0b 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.h
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.h
@@ -48,6 +48,7 @@
Camera2ClientBase(const sp<CameraService>& cameraService,
const sp<TCamCallbacks>& remoteCallback,
const String16& clientPackageName,
+ const std::unique_ptr<String16>& clientFeatureId,
const String8& cameraId,
int api1CameraId,
int cameraFacing,
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 98c1b5e..1026fdf 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -35,6 +35,8 @@
#include "device3/Camera3StreamInterface.h"
#include "binder/Status.h"
+#include "CameraOfflineSessionBase.h"
+
namespace android {
class CameraProviderManager;
@@ -383,6 +385,19 @@
* drop buffers for stream of streamId.
*/
virtual status_t dropStreamBuffers(bool /*dropping*/, int /*streamId*/) = 0;
+
+ /**
+ * Returns the maximum expected time it'll take for all currently in-flight
+ * 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;
};
}; // 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..3862521
--- /dev/null
+++ b/services/camera/libcameraservice/common/CameraOfflineSessionBase.h
@@ -0,0 +1,53 @@
+/*
+ * 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"
+
+namespace android {
+
+class CameraOfflineSessionBase : public virtual RefBase {
+ public:
+ virtual ~CameraOfflineSessionBase();
+
+ // The session's original camera ID
+ virtual const String8& getId() const = 0;
+
+ virtual status_t disconnect() = 0;
+
+ virtual status_t dump(int fd) = 0;
+
+ virtual status_t abort() = 0;
+
+ /**
+ * Capture result passing
+ */
+ virtual status_t waitForNextFrame(nsecs_t timeout) = 0;
+
+ virtual status_t getNextResult(CaptureResult *frame) = 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 fdb5657..0f74a48 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -40,6 +40,7 @@
#include <utils/Trace.h>
#include "api2/HeicCompositeStream.h"
+#include "device3/ZoomRatioMapper.h"
namespace android {
@@ -104,19 +105,30 @@
return OK;
}
-int CameraProviderManager::getCameraCount() const {
+std::pair<int, int> CameraProviderManager::getCameraCount() const {
std::lock_guard<std::mutex> lock(mInterfaceMutex);
- int count = 0;
+ int systemCameraCount = 0;
+ int publicCameraCount = 0;
for (auto& provider : mProviders) {
- for (auto& id : provider->mUniqueCameraIds) {
- // Hidden secure camera ids are not to be exposed to camera1 api.
- if (isPublicallyHiddenSecureCameraLocked(id)) {
+ for (auto &id : provider->mUniqueCameraIds) {
+ SystemCameraKind deviceKind = SystemCameraKind::PUBLIC;
+ if (getSystemCameraKindLocked(id, &deviceKind) != OK) {
+ ALOGE("%s: Invalid camera id %s, skipping", __FUNCTION__, id.c_str());
continue;
}
- count++;
+ switch(deviceKind) {
+ case SystemCameraKind::PUBLIC:
+ publicCameraCount++;
+ break;
+ case SystemCameraKind::SYSTEM_ONLY_CAMERA:
+ systemCameraCount++;
+ break;
+ default:
+ break;
+ }
}
}
- return count;
+ return std::make_pair(systemCameraCount, publicCameraCount);
}
std::vector<std::string> CameraProviderManager::getCameraDeviceIds() const {
@@ -130,25 +142,47 @@
return deviceIds;
}
+void CameraProviderManager::collectDeviceIdsLocked(const std::vector<std::string> deviceIds,
+ std::vector<std::string>& publicDeviceIds,
+ std::vector<std::string>& systemDeviceIds) const {
+ for (auto &deviceId : deviceIds) {
+ SystemCameraKind deviceKind = SystemCameraKind::PUBLIC;
+ if (getSystemCameraKindLocked(deviceId, &deviceKind) != OK) {
+ ALOGE("%s: Invalid camera id %s, skipping", __FUNCTION__, deviceId.c_str());
+ continue;
+ }
+ if (deviceKind == SystemCameraKind::SYSTEM_ONLY_CAMERA) {
+ systemDeviceIds.push_back(deviceId);
+ } else {
+ publicDeviceIds.push_back(deviceId);
+ }
+ }
+}
+
std::vector<std::string> CameraProviderManager::getAPI1CompatibleCameraDeviceIds() const {
std::lock_guard<std::mutex> lock(mInterfaceMutex);
+ std::vector<std::string> publicDeviceIds;
+ std::vector<std::string> systemDeviceIds;
std::vector<std::string> deviceIds;
for (auto& provider : mProviders) {
std::vector<std::string> providerDeviceIds = provider->mUniqueAPI1CompatibleCameraIds;
-
+ // Secure cameras should not be exposed through camera 1 api
+ providerDeviceIds.erase(std::remove_if(providerDeviceIds.begin(), providerDeviceIds.end(),
+ [this](const std::string& s) {
+ SystemCameraKind deviceKind = SystemCameraKind::PUBLIC;
+ if (getSystemCameraKindLocked(s, &deviceKind) != OK) {
+ ALOGE("%s: Invalid camera id %s, skipping", __FUNCTION__, s.c_str());
+ return true;
+ }
+ return deviceKind == SystemCameraKind::HIDDEN_SECURE_CAMERA;}),
+ providerDeviceIds.end());
// API1 app doesn't handle logical and physical camera devices well. So
// for each camera facing, only take the first id advertised by HAL in
// all [logical, physical1, physical2, ...] id combos, and filter out the rest.
filterLogicalCameraIdsLocked(providerDeviceIds);
- // Hidden secure camera ids are not to be exposed to camera1 api.
- providerDeviceIds.erase(std::remove_if(providerDeviceIds.begin(), providerDeviceIds.end(),
- [this](const std::string& s) {
- return this->isPublicallyHiddenSecureCameraLocked(s);}),
- providerDeviceIds.end());
- deviceIds.insert(deviceIds.end(), providerDeviceIds.begin(), providerDeviceIds.end());
+ collectDeviceIdsLocked(providerDeviceIds, publicDeviceIds, systemDeviceIds);
}
-
- std::sort(deviceIds.begin(), deviceIds.end(),
+ auto sortFunc =
[](const std::string& a, const std::string& b) -> bool {
uint32_t aUint = 0, bUint = 0;
bool aIsUint = base::ParseUint(a, &aUint);
@@ -164,7 +198,13 @@
}
// Simple string compare if both id are not uint
return a < b;
- });
+ };
+ // We put device ids for system cameras at the end since they will be pared
+ // off for processes not having system camera permissions.
+ std::sort(publicDeviceIds.begin(), publicDeviceIds.end(), sortFunc);
+ std::sort(systemDeviceIds.begin(), systemDeviceIds.end(), sortFunc);
+ deviceIds.insert(deviceIds.end(), publicDeviceIds.begin(), publicDeviceIds.end());
+ deviceIds.insert(deviceIds.end(), systemDeviceIds.begin(), systemDeviceIds.end());
return deviceIds;
}
@@ -193,6 +233,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);
@@ -544,15 +593,23 @@
}
}
-bool CameraProviderManager::ProviderInfo::DeviceInfo3::isPublicallyHiddenSecureCamera() {
+SystemCameraKind CameraProviderManager::ProviderInfo::DeviceInfo3::getSystemCameraKind() {
camera_metadata_entry_t entryCap;
entryCap = mCameraCharacteristics.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
- if (entryCap.count != 1) {
- // Do NOT hide this camera device if the capabilities specify anything more
- // than ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA.
- return false;
+ if (entryCap.count == 1 &&
+ entryCap.data.u8[0] == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA) {
+ return SystemCameraKind::HIDDEN_SECURE_CAMERA;
}
- return entryCap.data.u8[0] == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA;
+
+ // Go through the capabilities and check if it has
+ // ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA
+ for (size_t i = 0; i < entryCap.count; ++i) {
+ uint8_t capability = entryCap.data.u8[i];
+ if (capability == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA) {
+ return SystemCameraKind::SYSTEM_ONLY_CAMERA;
+ }
+ }
+ return SystemCameraKind::PUBLIC;
}
void CameraProviderManager::ProviderInfo::DeviceInfo3::getSupportedSizes(
@@ -659,31 +716,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;
@@ -731,11 +763,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)),
@@ -1056,15 +1083,18 @@
return deviceInfo->mIsLogicalCamera;
}
-bool CameraProviderManager::isPublicallyHiddenSecureCamera(const std::string& id) const {
+status_t CameraProviderManager::getSystemCameraKind(const std::string& id,
+ SystemCameraKind *kind) const {
std::lock_guard<std::mutex> lock(mInterfaceMutex);
- return isPublicallyHiddenSecureCameraLocked(id);
+ return getSystemCameraKindLocked(id, kind);
}
-bool CameraProviderManager::isPublicallyHiddenSecureCameraLocked(const std::string& id) const {
+status_t CameraProviderManager::getSystemCameraKindLocked(const std::string& id,
+ SystemCameraKind *kind) const {
auto deviceInfo = findDeviceInfoLocked(id);
if (deviceInfo != nullptr) {
- return deviceInfo->mIsPublicallyHiddenSecureCamera;
+ *kind = deviceInfo->mSystemCameraKind;
+ return OK;
}
// If this is a hidden physical camera, we should return what kind of
// camera the enclosing logical camera is.
@@ -1073,10 +1103,10 @@
LOG_ALWAYS_FATAL_IF(id == isHiddenAndParent.second->mId,
"%s: hidden physical camera id %s and enclosing logical camera id %s are the same",
__FUNCTION__, id.c_str(), isHiddenAndParent.second->mId.c_str());
- return isPublicallyHiddenSecureCameraLocked(isHiddenAndParent.second->mId);
+ return getSystemCameraKindLocked(isHiddenAndParent.second->mId, kind);
}
- // Invalid camera id
- return true;
+ // Neither a hidden physical camera nor a logical camera
+ return NAME_NOT_FOUND;
}
bool CameraProviderManager::isHiddenPhysicalCamera(const std::string& cameraId) const {
@@ -1137,7 +1167,7 @@
}
sp<provider::V2_4::ICameraProvider> interface;
- interface = mServiceProxy->getService(newProvider);
+ interface = mServiceProxy->tryGetService(newProvider);
if (interface == nullptr) {
ALOGE("%s: Camera provider HAL '%s' is not actually available", __FUNCTION__,
@@ -1966,7 +1996,7 @@
return;
}
- mIsPublicallyHiddenSecureCamera = isPublicallyHiddenSecureCamera();
+ mSystemCameraKind = getSystemCameraKind();
status_t res = fixupMonochromeTags();
if (OK != res) {
@@ -1985,6 +2015,13 @@
__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);
if (flashAvailable.count == 1 &&
@@ -2045,6 +2082,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);
+ }
}
}
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index 954c0d9..58df0e8 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -54,6 +54,26 @@
sp<VendorTagDescriptor>& descriptor);
};
+enum SystemCameraKind {
+ /**
+ * These camera devices are visible to all apps and system components alike
+ */
+ PUBLIC = 0,
+
+ /**
+ * These camera devices are visible only to processes having the
+ * android.permission.SYSTEM_CAMERA permission. They are not exposed to 3P
+ * apps.
+ */
+ SYSTEM_ONLY_CAMERA,
+
+ /**
+ * These camera devices are visible only to HAL clients (that try to connect
+ * on a hwbinder thread).
+ */
+ HIDDEN_SECURE_CAMERA
+};
+
/**
* A manager for all camera providers available on an Android device.
*
@@ -76,6 +96,10 @@
const std::string &serviceName,
const sp<hidl::manager::V1_0::IServiceNotification>
¬ification) = 0;
+ // Will not wait for service to start if it's not already running
+ virtual sp<hardware::camera::provider::V2_4::ICameraProvider> tryGetService(
+ const std::string &serviceName) = 0;
+ // Will block for service if it exists but isn't running
virtual sp<hardware::camera::provider::V2_4::ICameraProvider> getService(
const std::string &serviceName) = 0;
virtual hardware::hidl_vec<hardware::hidl_string> listServices() = 0;
@@ -92,6 +116,10 @@
return hardware::camera::provider::V2_4::ICameraProvider::registerForNotifications(
serviceName, notification);
}
+ virtual sp<hardware::camera::provider::V2_4::ICameraProvider> tryGetService(
+ const std::string &serviceName) override {
+ return hardware::camera::provider::V2_4::ICameraProvider::tryGetService(serviceName);
+ }
virtual sp<hardware::camera::provider::V2_4::ICameraProvider> getService(
const std::string &serviceName) override {
return hardware::camera::provider::V2_4::ICameraProvider::getService(serviceName);
@@ -132,10 +160,10 @@
ServiceInteractionProxy *proxy = &sHardwareServiceInteractionProxy);
/**
- * Retrieve the total number of available cameras. This value may change dynamically as cameras
- * are added or removed.
+ * Retrieve the total number of available cameras.
+ * This value may change dynamically as cameras are added or removed.
*/
- int getCameraCount() const;
+ std::pair<int, int> getCameraCount() const;
std::vector<std::string> getCameraDeviceIds() const;
@@ -159,6 +187,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,
@@ -272,8 +305,7 @@
*/
bool isLogicalCamera(const std::string& id, std::vector<std::string>* physicalCameraIds);
- bool isPublicallyHiddenSecureCamera(const std::string& id) const;
-
+ status_t getSystemCameraKind(const std::string& id, SystemCameraKind *kind) const;
bool isHiddenPhysicalCamera(const std::string& cameraId) const;
static const float kDepthARTolerance;
@@ -380,7 +412,7 @@
std::vector<std::string> mPhysicalIds;
hardware::CameraInfo mInfo;
sp<IBase> mSavedInterface;
- bool mIsPublicallyHiddenSecureCamera = false;
+ SystemCameraKind mSystemCameraKind = SystemCameraKind::PUBLIC;
const hardware::camera::common::V1_0::CameraResourceCost mResourceCost;
@@ -389,6 +421,7 @@
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;
@@ -422,10 +455,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>
@@ -498,7 +532,7 @@
CameraMetadata mCameraCharacteristics;
std::unordered_map<std::string, CameraMetadata> mPhysicalCameraCharacteristics;
void queryPhysicalCameraIds();
- bool isPublicallyHiddenSecureCamera();
+ SystemCameraKind getSystemCameraKind();
status_t fixupMonochromeTags();
status_t addDynamicDepthTags();
static void getSupportedSizes(const CameraMetadata& ch, uint32_t tag,
@@ -511,7 +545,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,
@@ -595,15 +628,14 @@
status_t getCameraCharacteristicsLocked(const std::string &id,
CameraMetadata* characteristics) const;
-
- bool isPublicallyHiddenSecureCameraLocked(const std::string& id) const;
-
void filterLogicalCameraIdsLocked(std::vector<std::string>& deviceIds) const;
- bool isPublicallyHiddenSecureCameraLocked(const std::string& id);
+ status_t getSystemCameraKindLocked(const std::string& id, SystemCameraKind *kind) const;
+ std::pair<bool, ProviderInfo::DeviceInfo *> isHiddenPhysicalCameraInternal(const std::string& cameraId) const;
- std::pair<bool, CameraProviderManager::ProviderInfo::DeviceInfo *>
- isHiddenPhysicalCameraInternal(const std::string& cameraId) const;
+ void collectDeviceIdsLocked(const std::vector<std::string> deviceIds,
+ std::vector<std::string>& normalDeviceIds,
+ std::vector<std::string>& systemCameraDeviceIds) const;
};
} // 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/device1/CameraHardwareInterface.cpp b/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp
index 522d521..62ef681 100644
--- a/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp
+++ b/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp
@@ -165,8 +165,12 @@
mem = mHidlMemPoolMap.at(data);
}
sp<CameraHeapMemory> heapMem(static_cast<CameraHeapMemory *>(mem->handle));
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
VideoNativeHandleMetadata* md = (VideoNativeHandleMetadata*)
- heapMem->mBuffers[bufferIndex]->pointer();
+ heapMem->mBuffers[bufferIndex]->unsecurePointer();
md->pHandle = const_cast<native_handle_t*>(frameData.getNativeHandle());
sDataCbTimestamp(timestamp, (int32_t) msgType, mem, bufferIndex, this);
return hardware::Void();
@@ -192,8 +196,12 @@
hidl_msg.bufferIndex, mem->mNumBufs);
return hardware::Void();
}
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
VideoNativeHandleMetadata* md = (VideoNativeHandleMetadata*)
- mem->mBuffers[hidl_msg.bufferIndex]->pointer();
+ mem->mBuffers[hidl_msg.bufferIndex]->unsecurePointer();
md->pHandle = const_cast<native_handle_t*>(hidl_msg.frameData.getNativeHandle());
msgs.push_back({hidl_msg.timestamp, mem->mBuffers[hidl_msg.bufferIndex]});
@@ -578,7 +586,11 @@
int bufferIndex = offset / size;
if (CC_LIKELY(mHidlDevice != nullptr)) {
if (size == sizeof(VideoNativeHandleMetadata)) {
- VideoNativeHandleMetadata* md = (VideoNativeHandleMetadata*) mem->pointer();
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ VideoNativeHandleMetadata* md = (VideoNativeHandleMetadata*) mem->unsecurePointer();
// Caching the handle here because md->pHandle will be subject to HAL's edit
native_handle_t* nh = md->pHandle;
hidl_handle frame = nh;
@@ -605,7 +617,11 @@
if (size == sizeof(VideoNativeHandleMetadata)) {
uint32_t heapId = heap->getHeapID();
uint32_t bufferIndex = offset / size;
- VideoNativeHandleMetadata* md = (VideoNativeHandleMetadata*) mem->pointer();
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ VideoNativeHandleMetadata* md = (VideoNativeHandleMetadata*) mem->unsecurePointer();
// Caching the handle here because md->pHandle will be subject to HAL's edit
native_handle_t* nh = md->pHandle;
VideoFrameMessage msg;
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index bda35f3..92ba5e4 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -58,6 +58,7 @@
#include "device3/Camera3InputStream.h"
#include "device3/Camera3DummyStream.h"
#include "device3/Camera3SharedOutputStream.h"
+#include "device3/Camera3OfflineSession.h"
#include "CameraService.h"
#include "utils/CameraThreadState.h"
@@ -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,18 @@
}
}
- 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);
+
return OK;
}
@@ -2116,6 +2137,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;
}
@@ -2806,6 +2836,27 @@
mOperatingMode = operatingMode;
}
+ // In case called from configureStreams, abort queued input buffers not belonging to
+ // any pending requests.
+ if (mInputStream != NULL && notifyRequestThread) {
+ while (true) {
+ camera3_stream_buffer_t inputBuffer;
+ status_t res = mInputStream->getInputBuffer(&inputBuffer,
+ /*respectHalLimit*/ false);
+ if (res != OK) {
+ // Exhausted acquiring all input buffers.
+ break;
+ }
+
+ inputBuffer.status = CAMERA3_BUFFER_STATUS_ERROR;
+ res = mInputStream->returnInputBuffer(inputBuffer);
+ if (res != OK) {
+ ALOGE("%s: %d: couldn't return input buffer while clearing input queue: "
+ "%s (%d)", __FUNCTION__, __LINE__, strerror(-res), res);
+ }
+ }
+ }
+
if (!mNeedConfig) {
ALOGV("%s: Skipping config, no stream changes", __FUNCTION__);
return OK;
@@ -3121,14 +3172,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, const std::set<std::string>& cameraIdsWithZoom,
+ const SurfaceMap& outputSurfaces) {
ATRACE_CALL();
Mutex::Autolock l(mInFlightLock);
ssize_t res;
res = mInFlightMap.add(frameNumber, InFlightRequest(numBuffers, resultExtras, hasInput,
hasAppCallback, maxExpectedDuration, physicalCameraIds, isStillCapture, isZslCapture,
- outputSurfaces));
+ cameraIdsWithZoom, outputSurfaces));
if (res < 0) return res;
if (mInFlightMap.size() == 1) {
@@ -3479,7 +3531,7 @@
CaptureResultExtras &resultExtras,
CameraMetadata &collectedPartialResult,
uint32_t frameNumber,
- bool reprocess, bool zslStillCapture,
+ bool reprocess, bool zslStillCapture, const std::set<std::string>& cameraIdsWithZoom,
const std::vector<PhysicalCaptureResultInfo>& physicalMetadatas) {
ATRACE_CALL();
if (pendingMetadata.isEmpty())
@@ -3548,19 +3600,39 @@
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);
+ frameNumber, strerror(-res), res);
return;
}
+
+ // Fix up result metadata to account for zoom ratio availabilities between
+ // HAL and app.
+ bool zoomRatioIs1 = cameraIdsWithZoom.find(mId.c_str()) == cameraIdsWithZoom.end();
+ res = mZoomRatioMappers[mId.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;
+ }
+
for (auto& physicalMetadata : captureResult.mPhysicalMetadatas) {
String8 cameraId8(physicalMetadata.mPhysicalCameraId);
- if (mDistortionMappers.find(cameraId8.c_str()) == mDistortionMappers.end()) {
- continue;
+ if (mDistortionMappers.find(cameraId8.c_str()) != mDistortionMappers.end()) {
+ 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;
+ }
}
- res = mDistortionMappers[cameraId8.c_str()].correctCaptureResult(
- &physicalMetadata.mPhysicalCameraMetadata);
+
+ zoomRatioIs1 = cameraIdsWithZoom.find(cameraId8.c_str()) == cameraIdsWithZoom.end();
+ res = mZoomRatioMappers[cameraId8.c_str()].updateCaptureResult(
+ &physicalMetadata.mPhysicalCameraMetadata, zoomRatioIs1);
if (res != OK) {
- SET_ERR("Unable to correct physical capture result metadata for frame %d: %s (%d)",
- frameNumber, strerror(res), res);
+ 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;
}
}
@@ -3683,7 +3755,7 @@
// 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("Requested physical Camera Ids %d not equal to number of metadata %d",
+ SET_ERR("Expected physical Camera metadata count %d not equal to actual count %d",
request.physicalCameraIds.size(), result->num_physcam_metadata);
return;
}
@@ -3765,7 +3837,7 @@
sendCaptureResult(metadata, request.resultExtras,
collectedPartialResult, frameNumber,
hasInputBufferInRequest, request.zslCapture && request.stillCapture,
- request.physicalMetadatas);
+ request.cameraIdsWithZoom, request.physicalMetadatas);
}
}
@@ -3877,12 +3949,14 @@
errorCode) {
if (physicalCameraId.size() > 0) {
String8 cameraId(physicalCameraId);
- if (r.physicalCameraIds.find(cameraId) == r.physicalCameraIds.end()) {
+ 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;
@@ -3981,7 +4055,7 @@
sendCaptureResult(r.pendingMetadata, r.resultExtras,
r.collectedPartialResult, msg.frame_number,
r.hasInputBuffer, r.zslCapture && r.stillCapture,
- r.physicalMetadatas);
+ r.cameraIdsWithZoom, r.physicalMetadatas);
}
bool timestampIncreasing = !(r.zslCapture || r.hasInputBuffer);
returnOutputBuffers(r.pendingOutputBuffers.array(),
@@ -4026,13 +4100,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;
@@ -4047,18 +4126,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();
@@ -4734,6 +4817,38 @@
}
}
+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) {
+ 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) {
+ ALOGE("%s: offlineSessionInfo and offlineSession 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;
+ }
+ return CameraProviderManager::mapToStatusT(status);
+}
+
void Camera3Device::HalInterface::getInflightBufferKeys(
std::vector<std::pair<int32_t, int32_t>>* out) {
std::lock_guard<std::mutex> lock(mInflightLock);
@@ -5086,6 +5201,7 @@
ALOGW("%s: %d: couldn't get input buffer while clearing the request "
"list: %s (%d)", __FUNCTION__, __LINE__, strerror(-res), res);
} else {
+ inputBuffer.status = CAMERA3_BUFFER_STATUS_ERROR;
res = (*it)->mInputStream->returnInputBuffer(inputBuffer);
if (res != OK) {
ALOGE("%s: %d: couldn't return input buffer while clearing the request "
@@ -5110,6 +5226,7 @@
*lastFrameNumber = mRepeatingLastFrameNumber;
}
mRepeatingLastFrameNumber = hardware::camera2::ICameraDeviceUser::NO_IN_FLIGHT_REPEATING_FRAMES;
+ mRequestSignal.signal();
return OK;
}
@@ -5506,6 +5623,7 @@
Mutex::Autolock l(mRequestLock);
mNextRequests.clear();
}
+ mRequestSubmittedSignal.signal();
return submitRequestSuccess;
}
@@ -5542,6 +5660,7 @@
// 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
@@ -5574,6 +5693,28 @@
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;
+ }
+ }
}
}
@@ -5584,6 +5725,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() {
@@ -5771,7 +5913,7 @@
/*hasInput*/halRequest->input_buffer != NULL,
hasCallback,
calculateMaxExpectedDuration(halRequest->settings),
- requestedPhysicalCameras, isStillCapture, isZslCapture,
+ requestedPhysicalCameras, isStillCapture, isZslCapture, mPrevCameraIdsWithZoom,
(mUseHalBufManager) ? uniqueSurfaceIdMap :
SurfaceMap{});
ALOGVV("%s: registered in flight requestId = %" PRId32 ", frameNumber = %" PRId64
@@ -5885,6 +6027,32 @@
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) {
+ Mutex::Autolock l(mRequestLock);
+ clearRepeatingRequestsLocked(/*lastFrameNumber*/nullptr);
+
+ // Theoretically we should also check for mRepeatingRequests.empty(), but the API interface
+ // is serialized by mInterfaceLock so 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 a 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);
+}
+
nsecs_t Camera3Device::getExpectedInFlightDuration() {
ATRACE_CALL();
Mutex::Autolock al(mInFlightLock);
@@ -6794,4 +6962,34 @@
return res;
}
+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;
+ }
+
+ Mutex::Autolock il(mInterfaceLock);
+ Mutex::Autolock l(mLock);
+
+ // 1. Stop repeating request, wait until request thread submitted all requests, then
+ // call HAL switchToOffline
+ hardware::camera::device::V3_6::CameraOfflineSessionInfo offlineSessionInfo;
+ sp<hardware::camera::device::V3_6::ICameraOfflineSession> offlineSession;
+
+ mRequestThread->switchToOffline(streamsToKeep, &offlineSessionInfo, &offlineSession);
+
+
+ // 2. Verify offlineSessionInfo
+ // 3. create Camera3OfflineSession and transfer object ownership
+ // (streams, inflight requests, buffer caches)
+ *session = new Camera3OfflineSession(mId);
+
+ // 4. Delete unneeded streams
+ // 5. Verify Camera3Device is in unconfigured state
+ return OK;
+}
+
}; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index cae34ce..5faabd1 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>
@@ -45,6 +46,7 @@
#include "device3/StatusTracker.h"
#include "device3/Camera3BufferManager.h"
#include "device3/DistortionMapper.h"
+#include "device3/ZoomRatioMapper.h"
#include "utils/TagMonitor.h"
#include "utils/LatencyHistogram.h"
#include <camera_metadata_hidden.h>
@@ -194,6 +196,11 @@
*/
status_t dropStreamBuffers(bool dropping, int streamId) override;
+ nsecs_t getExpectedInFlightDuration() override;
+
+ status_t switchToOffline(const std::vector<int32_t>& streamsToKeep,
+ /*out*/ sp<CameraOfflineSessionBase>* session) override;
+
/**
* Helper functions to map between framework and HIDL values
*/
@@ -280,7 +287,7 @@
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();
@@ -314,6 +321,11 @@
bool isReconfigurationRequired(CameraMetadata& oldSessionParams,
CameraMetadata& newSessionParams);
+ 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);
+
// 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);
@@ -350,6 +362,8 @@
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;
@@ -422,11 +436,14 @@
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];
@@ -840,6 +857,11 @@
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);
+
protected:
virtual bool threadLoop();
@@ -860,6 +882,9 @@
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;
@@ -934,6 +959,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
@@ -956,6 +982,7 @@
sp<CaptureRequest> mPrevRequest;
int32_t mPrevTriggers;
+ std::set<std::string> mPrevCameraIdsWithZoom;
uint32_t mFrameNumber;
@@ -1054,6 +1081,9 @@
// Indicates a ZSL capture request
bool zslCapture;
+ // 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;
@@ -1075,7 +1105,7 @@
InFlightRequest(int numBuffers, CaptureResultExtras extras, bool hasInput,
bool hasAppCallback, nsecs_t maxDuration,
const std::set<String8>& physicalCameraIdSet, bool isStillCapture,
- bool isZslCapture,
+ bool isZslCapture, const std::set<std::string>& idsWithZoom,
const SurfaceMap& outSurfaces = SurfaceMap{}) :
shutterTimestamp(0),
sensorTimestamp(0),
@@ -1090,6 +1120,7 @@
physicalCameraIds(physicalCameraIdSet),
stillCapture(isStillCapture),
zslCapture(isZslCapture),
+ cameraIdsWithZoom(idsWithZoom),
outputSurfaces(outSurfaces) {
}
};
@@ -1107,16 +1138,10 @@
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,
+ bool isStillCapture, bool isZslCapture, const std::set<std::string>& cameraIdsWithZoom,
const SurfaceMap& outputSurfaces);
/**
- * Returns the maximum expected time it'll take for all currently in-flight
- * requests to complete, based on their settings
- */
- nsecs_t getExpectedInFlightDuration();
-
- /**
* Tracking for idle detection
*/
sp<camera3::StatusTracker> mStatusTracker;
@@ -1238,6 +1263,7 @@
CaptureResultExtras &resultExtras,
CameraMetadata &collectedPartialResult, uint32_t frameNumber,
bool reprocess, bool zslStillCapture,
+ const std::set<std::string>& cameraIdsWithZoom,
const std::vector<PhysicalCaptureResultInfo>& physicalMetadatas);
bool isLastFullResult(const InFlightRequest& inFlightRequest);
@@ -1270,6 +1296,11 @@
// 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;
+
// 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
@@ -1368,6 +1399,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/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
index fc83684..cb59a76 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
@@ -71,7 +71,8 @@
res = mConsumer->acquireBuffer(&bufferItem, /*waitForFence*/false);
if (res != OK) {
- ALOGE("%s: Stream %d: Can't acquire next output buffer: %s (%d)",
+ // This may or may not be an error condition depending on caller.
+ ALOGV("%s: Stream %d: Can't acquire next output buffer: %s (%d)",
__FUNCTION__, mId, strerror(-res), res);
return res;
}
diff --git a/services/camera/libcameraservice/device3/Camera3OfflineSession.cpp b/services/camera/libcameraservice/device3/Camera3OfflineSession.cpp
new file mode 100644
index 0000000..dc5cbb3
--- /dev/null
+++ b/services/camera/libcameraservice/device3/Camera3OfflineSession.cpp
@@ -0,0 +1,118 @@
+/*
+ * 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 "device3/Camera3OfflineSession.h"
+#include "device3/Camera3OutputStream.h"
+#include "device3/Camera3InputStream.h"
+#include "device3/Camera3SharedOutputStream.h"
+
+using namespace android::camera3;
+using namespace android::hardware::camera;
+
+namespace android {
+
+Camera3OfflineSession::Camera3OfflineSession(const String8 &id):
+ mId(id)
+{
+ 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());
+}
+
+const String8& Camera3OfflineSession::getId() const {
+ return mId;
+}
+
+status_t Camera3OfflineSession::initialize(
+ sp<hardware::camera::device::V3_6::ICameraOfflineSession> /*hidlSession*/) {
+ ATRACE_CALL();
+ return OK;
+}
+
+status_t Camera3OfflineSession::dump(int /*fd*/) {
+ ATRACE_CALL();
+ return OK;
+}
+
+status_t Camera3OfflineSession::abort() {
+ ATRACE_CALL();
+ return OK;
+}
+
+status_t Camera3OfflineSession::disconnect() {
+ ATRACE_CALL();
+ return OK;
+}
+
+status_t Camera3OfflineSession::waitForNextFrame(nsecs_t /*timeout*/) {
+ ATRACE_CALL();
+ return OK;
+}
+
+status_t Camera3OfflineSession::getNextResult(CaptureResult* /*frame*/) {
+ ATRACE_CALL();
+ return OK;
+}
+
+hardware::Return<void> Camera3OfflineSession::processCaptureResult_3_4(
+ const hardware::hidl_vec<
+ hardware::camera::device::V3_4::CaptureResult>& /*results*/) {
+ return hardware::Void();
+}
+
+hardware::Return<void> Camera3OfflineSession::processCaptureResult(
+ const hardware::hidl_vec<
+ hardware::camera::device::V3_2::CaptureResult>& /*results*/) {
+ return hardware::Void();
+}
+
+hardware::Return<void> Camera3OfflineSession::notify(
+ const hardware::hidl_vec<hardware::camera::device::V3_2::NotifyMsg>& /*msgs*/) {
+ return hardware::Void();
+}
+
+hardware::Return<void> Camera3OfflineSession::requestStreamBuffers(
+ const hardware::hidl_vec<hardware::camera::device::V3_5::BufferRequest>& /*bufReqs*/,
+ requestStreamBuffers_cb /*_hidl_cb*/) {
+ return hardware::Void();
+}
+
+hardware::Return<void> Camera3OfflineSession::returnStreamBuffers(
+ const hardware::hidl_vec<hardware::camera::device::V3_2::StreamBuffer>& /*buffers*/) {
+ return hardware::Void();
+}
+
+}; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3OfflineSession.h b/services/camera/libcameraservice/device3/Camera3OfflineSession.h
new file mode 100644
index 0000000..30e8c4f
--- /dev/null
+++ b/services/camera/libcameraservice/device3/Camera3OfflineSession.h
@@ -0,0 +1,126 @@
+/*
+ * 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 <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 "utils/TagMonitor.h"
+#include "utils/LatencyHistogram.h"
+#include <camera_metadata_hidden.h>
+
+namespace android {
+
+namespace camera3 {
+
+class Camera3Stream;
+class Camera3OutputStreamInterface;
+class Camera3StreamInterface;
+
+} // namespace camera3
+
+/**
+ * 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:
+
+ // initialize by Camera3Device. Camera3Device must send all info in separate argument.
+ // monitored tags
+ // mUseHalBufManager
+ // mUsePartialResult
+ // mNumPartialResults
+ explicit Camera3OfflineSession(const String8& id);
+
+ virtual ~Camera3OfflineSession();
+
+ status_t initialize(
+ sp<hardware::camera::device::V3_6::ICameraOfflineSession> hidlSession);
+
+ /**
+ * CameraOfflineSessionBase interface
+ */
+ const String8& getId() const override;
+
+ status_t disconnect() override;
+
+ status_t dump(int fd) override;
+
+ status_t abort() override;
+
+ // methods for capture result passing
+ 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;
+
+}; // class Camera3OfflineSession
+
+}; // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index fd9b4b0..f707ef8 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -70,7 +70,7 @@
mFormatOverridden(false),
mOriginalFormat(format),
mDataSpaceOverridden(false),
- mOriginalDataSpace(HAL_DATASPACE_UNKNOWN),
+ mOriginalDataSpace(dataSpace),
mPhysicalCameraId(physicalCameraId),
mLastTimestamp(0) {
@@ -137,9 +137,6 @@
void Camera3Stream::setDataSpaceOverride(bool dataSpaceOverridden) {
mDataSpaceOverridden = dataSpaceOverridden;
- if (dataSpaceOverridden && mOriginalDataSpace == HAL_DATASPACE_UNKNOWN) {
- mOriginalDataSpace = camera3_stream::data_space;
- }
}
bool Camera3Stream::isDataSpaceOverridden() const {
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 67afd0f..805df82 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -588,7 +588,7 @@
//Keep track of original dataSpace in case it gets overridden
bool mDataSpaceOverridden;
- android_dataspace mOriginalDataSpace;
+ const android_dataspace mOriginalDataSpace;
String8 mPhysicalCameraId;
nsecs_t mLastTimestamp;
diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
index 3089181..5c6c518 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
+++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
@@ -498,7 +498,7 @@
mInputSlots[bufferItem.mSlot].mFrameNumber = bufferItem.mFrameNumber;
} else {
SP_LOGE("%s: Invalid input graphic buffer!", __FUNCTION__);
- res = BAD_VALUE;
+ mOnFrameAvailableRes.store(BAD_VALUE);
return;
}
bufferId = bufferItem.mGraphicBuffer->getId();
@@ -543,6 +543,11 @@
mOnFrameAvailableRes.store(res);
}
+void Camera3StreamSplitter::onFrameReplaced(const BufferItem& item) {
+ ATRACE_CALL();
+ onFrameAvailable(item);
+}
+
void Camera3StreamSplitter::decrementBufRefCountLocked(uint64_t id, size_t surfaceId) {
ATRACE_CALL();
diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.h b/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
index 960f7aa..4eb455a 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
@@ -102,6 +102,13 @@
void onFrameAvailable(const BufferItem& item) override;
// From IConsumerListener
+ //
+ // Similar to onFrameAvailable, but buffer item is indeed replacing a buffer
+ // in the buffer queue. This can happen when buffer queue is in droppable
+ // mode.
+ void onFrameReplaced(const BufferItem& item) override;
+
+ // From IConsumerListener
// We don't care about released buffers because we detach each buffer as
// soon as we acquire it. See the comment for onBufferReleased below for
// some clarifying notes about the name.
diff --git a/services/camera/libcameraservice/device3/CoordinateMapper.cpp b/services/camera/libcameraservice/device3/CoordinateMapper.cpp
new file mode 100644
index 0000000..d62f397
--- /dev/null
+++ b/services/camera/libcameraservice/device3/CoordinateMapper.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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
+ */
+
+// 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
+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..a255003 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,7 +32,7 @@
* 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();
@@ -150,20 +151,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;
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..16b223b
--- /dev/null
+++ b/services/camera/libcameraservice/device3/ZoomRatioMapper.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.
+ */
+
+#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) {}
+
+ /**
+ * 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/HidlCameraService.cpp b/services/camera/libcameraservice/hidl/HidlCameraService.cpp
index 1daa035..97ba9c4 100644
--- a/services/camera/libcameraservice/hidl/HidlCameraService.cpp
+++ b/services/camera/libcameraservice/hidl/HidlCameraService.cpp
@@ -103,7 +103,7 @@
}
sp<hardware::camera2::ICameraDeviceCallbacks> callbacks = hybridCallbacks;
binder::Status serviceRet = mAidlICameraService->connectDevice(
- callbacks, String16(cameraId.c_str()), String16(""),
+ callbacks, String16(cameraId.c_str()), String16(""), std::unique_ptr<String16>(),
hardware::ICameraService::USE_CALLING_UID, /*out*/&deviceRemote);
HStatus status = HStatus::NO_ERROR;
if (!serviceRet.isOk()) {
diff --git a/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp b/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
index 78d737d..084dc62 100644
--- a/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
+++ b/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
@@ -183,6 +183,7 @@
sp<TestICameraProvider> mTestCameraProvider;
TestInteractionProxy() {}
+
void setProvider(sp<TestICameraProvider> provider) {
mTestCameraProvider = provider;
}
@@ -199,13 +200,31 @@
return true;
}
+ virtual sp<hardware::camera::provider::V2_4::ICameraProvider> tryGetService(
+ const std::string &serviceName) override {
+ // If no provider has been given, act like the HAL isn't available and return null.
+ if (mTestCameraProvider == nullptr) return nullptr;
+ return getService(serviceName);
+ }
+
virtual sp<hardware::camera::provider::V2_4::ICameraProvider> getService(
const std::string &serviceName) override {
+ // If no provider has been given, fail; in reality, getService would
+ // block for HALs that don't start correctly, so we should never use
+ // getService when we don't have a valid HAL running
+ if (mTestCameraProvider == nullptr) {
+ ADD_FAILURE() << "getService called with no valid provider; would block indefinitely";
+ // Real getService would block, but that's bad in unit tests. So
+ // just record an error and return nullptr
+ return nullptr;
+ }
mLastRequestedServiceNames.push_back(serviceName);
return mTestCameraProvider;
}
virtual hardware::hidl_vec<hardware::hidl_string> listServices() override {
+ // Always provide a list even if there's no actual provider yet, to
+ // simulate stuck HAL situations as well
hardware::hidl_vec<hardware::hidl_string> ret = {"test/0"};
return ret;
}
@@ -438,3 +457,52 @@
<< "Unable to change device state";
}
+
+// Test that CameraProviderManager doesn't get stuck when the camera HAL isn't really working
+TEST(CameraProviderManagerTest, BadHalStartupTest) {
+
+ std::vector<hardware::hidl_string> deviceNames;
+ deviceNames.push_back("device@3.2/test/0");
+ deviceNames.push_back("device@1.0/test/0");
+ deviceNames.push_back("device@3.2/test/1");
+ hardware::hidl_vec<common::V1_0::VendorTagSection> vendorSection;
+ status_t res;
+
+ sp<CameraProviderManager> providerManager = new CameraProviderManager();
+ sp<TestStatusListener> statusListener = new TestStatusListener();
+ TestInteractionProxy serviceProxy;
+ sp<TestICameraProvider> provider = new TestICameraProvider(deviceNames,
+ vendorSection);
+
+ // Not setting up provider in the service proxy yet, to test cases where a
+ // HAL isn't starting right
+ res = providerManager->initialize(statusListener, &serviceProxy);
+ ASSERT_EQ(res, OK) << "Unable to initialize provider manager";
+
+ // Now set up provider and trigger a registration
+ serviceProxy.setProvider(provider);
+ int numProviders = static_cast<int>(serviceProxy.listServices().size());
+
+ hardware::hidl_string testProviderFqInterfaceName =
+ "android.hardware.camera.provider@2.4::ICameraProvider";
+ hardware::hidl_string testProviderInstanceName = "test/0";
+ serviceProxy.mManagerNotificationInterface->onRegistration(
+ testProviderFqInterfaceName,
+ testProviderInstanceName, false);
+
+ // Check that new provider is called once for all the init methods
+ EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::SET_CALLBACK], numProviders) <<
+ "Only one call to setCallback per provider expected during register";
+ EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::GET_VENDOR_TAGS], numProviders) <<
+ "Only one call to getVendorTags per provider expected during register";
+ EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::IS_SET_TORCH_MODE_SUPPORTED],
+ numProviders) <<
+ "Only one call to isSetTorchModeSupported per provider expected during init";
+ EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::GET_CAMERA_ID_LIST], numProviders) <<
+ "Only one call to getCameraIdList per provider expected during init";
+ EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::NOTIFY_DEVICE_STATE], numProviders) <<
+ "Only one call to notifyDeviceState per provider expected during init";
+
+ ASSERT_EQ(serviceProxy.mLastRequestedServiceNames.back(), testProviderInstanceName) <<
+ "Incorrect instance requested from service manager";
+}
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/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/mediaanalytics/Android.bp b/services/mediaanalytics/Android.bp
deleted file mode 100644
index c27aced..0000000
--- a/services/mediaanalytics/Android.bp
+++ /dev/null
@@ -1,68 +0,0 @@
-// Media Statistics service
-//
-
-cc_binary {
- name: "mediametrics",
-
- srcs: [
- "main_mediametrics.cpp",
- "MediaAnalyticsService.cpp",
- "iface_statsd.cpp",
- "statsd_audiopolicy.cpp",
- "statsd_audiorecord.cpp",
- "statsd_audiothread.cpp",
- "statsd_audiotrack.cpp",
- "statsd_codec.cpp",
- "statsd_drm.cpp",
- "statsd_extractor.cpp",
- "statsd_nuplayer.cpp",
- "statsd_recorder.cpp",
- ],
-
- proto: {
- type: "lite",
- },
-
- shared_libs: [
- "libcutils",
- "liblog",
- "libmedia",
- "libutils",
- "libbinder",
- "libdl",
- "libgui",
- "libmedia",
- "libmediautils",
- "libmediametrics",
- "libstagefright_foundation",
- "libstatslog",
- "libutils",
- "libprotobuf-cpp-lite",
- ],
-
- static_libs: [
- "libplatformprotos",
- "libregistermsext",
- ],
-
- include_dirs: [
- "frameworks/av/media/libstagefright/include",
- "frameworks/av/media/libstagefright/rtsp",
- "frameworks/av/media/libstagefright/webm",
- "frameworks/av/include/media",
- "frameworks/av/camera/include/camera",
- "frameworks/native/include/media/openmax",
- "frameworks/native/include/media/hardware",
- "external/tremolo/Tremolo",
- ],
-
- init_rc: ["mediametrics.rc"],
-
- cflags: [
- "-Werror",
- "-Wall",
- "-Wno-error=deprecated-declarations",
- ],
- clang: true,
-
-}
diff --git a/services/mediaanalytics/MediaAnalyticsService.cpp b/services/mediaanalytics/MediaAnalyticsService.cpp
deleted file mode 100644
index 0e7edfd..0000000
--- a/services/mediaanalytics/MediaAnalyticsService.cpp
+++ /dev/null
@@ -1,756 +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.
- */
-
-// Proxy for media player implementations
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "MediaAnalyticsService"
-#include <utils/Log.h>
-
-#include <stdint.h>
-#include <inttypes.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <dirent.h>
-#include <pthread.h>
-#include <unistd.h>
-
-#include <string.h>
-#include <pwd.h>
-
-#include <cutils/atomic.h>
-#include <cutils/properties.h> // for property_get
-
-#include <utils/misc.h>
-
-#include <android/content/pm/IPackageManagerNative.h>
-
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/MemoryHeapBase.h>
-#include <binder/MemoryBase.h>
-#include <gui/Surface.h>
-#include <utils/Errors.h> // for status_t
-#include <utils/List.h>
-#include <utils/String8.h>
-#include <utils/SystemClock.h>
-#include <utils/Timers.h>
-#include <utils/Vector.h>
-
-#include <media/IMediaHTTPService.h>
-#include <media/IRemoteDisplay.h>
-#include <media/IRemoteDisplayClient.h>
-#include <media/MediaPlayerInterface.h>
-#include <media/mediarecorder.h>
-#include <media/MediaMetadataRetrieverInterface.h>
-#include <media/Metadata.h>
-#include <media/AudioTrack.h>
-#include <media/MemoryLeakTrackUtil.h>
-#include <media/stagefright/MediaCodecList.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/Utils.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/ALooperRoster.h>
-#include <mediautils/BatteryNotifier.h>
-
-//#include <memunreachable/memunreachable.h>
-#include <system/audio.h>
-
-#include <private/android_filesystem_config.h>
-
-#include "MediaAnalyticsService.h"
-
-namespace android {
-
-// individual records kept in memory: age or count
-// age: <= 28 hours (1 1/6 days)
-// count: hard limit of # records
-// (0 for either of these disables that threshold)
-//
-static constexpr nsecs_t kMaxRecordAgeNs = 28 * 3600 * (1000*1000*1000ll);
-// 2019/6: average daily per device is currently 375-ish;
-// setting this to 2000 is large enough to catch most devices
-// we'll lose some data on very very media-active devices, but only for
-// the gms collection; statsd will have already covered those for us.
-// This also retains enough information to help with bugreports
-static constexpr int kMaxRecords = 2000;
-
-// max we expire in a single call, to constrain how long we hold the
-// mutex, which also constrains how long a client might wait.
-static constexpr int kMaxExpiredAtOnce = 50;
-
-// TODO: need to look at tuning kMaxRecords and friends for low-memory devices
-
-static const char *kServiceName = "media.metrics";
-
-void MediaAnalyticsService::instantiate() {
- defaultServiceManager()->addService(
- String16(kServiceName), new MediaAnalyticsService());
-}
-
-MediaAnalyticsService::MediaAnalyticsService()
- : mMaxRecords(kMaxRecords),
- mMaxRecordAgeNs(kMaxRecordAgeNs),
- mMaxRecordsExpiredAtOnce(kMaxExpiredAtOnce),
- mDumpProto(MediaAnalyticsItem::PROTO_V1),
- mDumpProtoDefault(MediaAnalyticsItem::PROTO_V1) {
-
- ALOGD("MediaAnalyticsService created");
-
- mItemsSubmitted = 0;
- mItemsFinalized = 0;
- mItemsDiscarded = 0;
- mItemsDiscardedExpire = 0;
- mItemsDiscardedCount = 0;
-
- mLastSessionID = 0;
- // recover any persistency we set up
- // etc
-}
-
-MediaAnalyticsService::~MediaAnalyticsService() {
- ALOGD("MediaAnalyticsService destroyed");
-
- while (mItems.size() > 0) {
- MediaAnalyticsItem * oitem = *(mItems.begin());
- mItems.erase(mItems.begin());
- delete oitem;
- mItemsDiscarded++;
- mItemsDiscardedCount++;
- }
-}
-
-
-MediaAnalyticsItem::SessionID_t MediaAnalyticsService::generateUniqueSessionID() {
- // generate a new sessionid
-
- Mutex::Autolock _l(mLock_ids);
- return (++mLastSessionID);
-}
-
-// caller surrenders ownership of 'item'
-MediaAnalyticsItem::SessionID_t MediaAnalyticsService::submit(MediaAnalyticsItem *item, bool forcenew)
-{
- UNUSED(forcenew);
-
- // fill in a sessionID if we do not yet have one
- if (item->getSessionID() <= MediaAnalyticsItem::SessionIDNone) {
- item->setSessionID(generateUniqueSessionID());
- }
-
- // we control these, generally not trusting user input
- nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
- // round nsecs to seconds
- now = ((now + 500000000) / 1000000000) * 1000000000;
- item->setTimestamp(now);
- int pid = IPCThreadState::self()->getCallingPid();
- int uid = IPCThreadState::self()->getCallingUid();
-
- int uid_given = item->getUid();
- int pid_given = item->getPid();
-
- // although we do make exceptions for some trusted client uids
- bool isTrusted = false;
-
- ALOGV("caller has uid=%d, embedded uid=%d", uid, uid_given);
-
- switch (uid) {
- case AID_MEDIA:
- case AID_MEDIA_CODEC:
- case AID_MEDIA_EX:
- case AID_MEDIA_DRM:
- // trusted source, only override default values
- isTrusted = true;
- if (uid_given == (-1)) {
- item->setUid(uid);
- }
- if (pid_given == (-1)) {
- item->setPid(pid);
- }
- break;
- default:
- isTrusted = false;
- item->setPid(pid);
- item->setUid(uid);
- break;
- }
-
- // Overwrite package name and version if the caller was untrusted.
- if (!isTrusted) {
- setPkgInfo(item, item->getUid(), true, true);
- } else if (item->getPkgName().empty()) {
- // empty, so fill out both parts
- setPkgInfo(item, item->getUid(), true, true);
- } else {
- // trusted, provided a package, do nothing
- }
-
- ALOGV("given uid %d; sanitized uid: %d sanitized pkg: %s "
- "sanitized pkg version: %" PRId64,
- uid_given, item->getUid(),
- item->getPkgName().c_str(),
- item->getPkgVersionCode());
-
- mItemsSubmitted++;
-
- // validate the record; we discard if we don't like it
- if (contentValid(item, isTrusted) == false) {
- delete item;
- return MediaAnalyticsItem::SessionIDInvalid;
- }
-
- // XXX: if we have a sessionid in the new record, look to make
- // sure it doesn't appear in the finalized list.
-
- if (item->count() == 0) {
- ALOGV("dropping empty record...");
- delete item;
- item = NULL;
- return MediaAnalyticsItem::SessionIDInvalid;
- }
-
- // save the new record
- //
- // send a copy to statsd
- dump2Statsd(item);
-
- // and keep our copy for dumpsys
- MediaAnalyticsItem::SessionID_t id = item->getSessionID();
- saveItem(item);
- mItemsFinalized++;
-
- return id;
-}
-
-
-status_t MediaAnalyticsService::dump(int fd, const Vector<String16>& args)
-{
- const size_t SIZE = 512;
- char buffer[SIZE];
- String8 result;
-
- if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
- snprintf(buffer, SIZE, "Permission Denial: "
- "can't dump MediaAnalyticsService from pid=%d, uid=%d\n",
- IPCThreadState::self()->getCallingPid(),
- IPCThreadState::self()->getCallingUid());
- result.append(buffer);
- write(fd, result.string(), result.size());
- return NO_ERROR;
- }
-
- // crack any parameters
- String16 protoOption("-proto");
- int chosenProto = mDumpProtoDefault;
- String16 clearOption("-clear");
- bool clear = false;
- String16 sinceOption("-since");
- nsecs_t ts_since = 0;
- String16 helpOption("-help");
- String16 onlyOption("-only");
- std::string only;
- int n = args.size();
-
- for (int i = 0; i < n; i++) {
- String8 myarg(args[i]);
- if (args[i] == clearOption) {
- clear = true;
- } 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");
- }
- } else {
- result.append("missing value for -proto\n\n");
- }
- } else if (args[i] == sinceOption) {
- i++;
- if (i < n) {
- String8 value(args[i]);
- char *endp;
- const char *p = value.string();
- ts_since = strtoll(p, &endp, 10);
- if (endp == p || *endp != '\0') {
- ts_since = 0;
- }
- } else {
- ts_since = 0;
- }
- // command line is milliseconds; internal units are nano-seconds
- ts_since *= 1000*1000;
- } else if (args[i] == onlyOption) {
- i++;
- if (i < n) {
- String8 value(args[i]);
- only = value.string();
- }
- } else if (args[i] == helpOption) {
- result.append("Recognized parameters:\n");
- result.append("-help this help message\n");
- result.append("-proto # dump using protocol #");
- result.append("-clear clears out saved records\n");
- result.append("-only X process records for component X\n");
- result.append("-since X include records since X\n");
- result.append(" (X is milliseconds since the UNIX epoch)\n");
- write(fd, result.string(), result.size());
- return NO_ERROR;
- }
- }
-
- Mutex::Autolock _l(mLock);
- // mutex between insertion and dumping the contents
-
- mDumpProto = chosenProto;
-
- // we ALWAYS dump this piece
- snprintf(buffer, SIZE, "Dump of the %s process:\n", kServiceName);
- result.append(buffer);
-
- dumpHeaders(result, ts_since);
-
- dumpRecent(result, ts_since, only.c_str());
-
-
- if (clear) {
- // remove everything from the finalized queue
- while (mItems.size() > 0) {
- MediaAnalyticsItem * oitem = *(mItems.begin());
- mItems.erase(mItems.begin());
- delete oitem;
- mItemsDiscarded++;
- }
-
- // shall we clear the summary data too?
-
- }
-
- write(fd, result.string(), result.size());
- return NO_ERROR;
-}
-
-// dump headers
-void MediaAnalyticsService::dumpHeaders(String8 &result, nsecs_t ts_since)
-{
- const size_t SIZE = 512;
- char buffer[SIZE];
-
- snprintf(buffer, SIZE, "Protocol Version: %d\n", mDumpProto);
- result.append(buffer);
-
- int enabled = MediaAnalyticsItem::isEnabled();
- if (enabled) {
- snprintf(buffer, SIZE, "Metrics gathering: enabled\n");
- } else {
- snprintf(buffer, SIZE, "Metrics gathering: DISABLED via property\n");
- }
- result.append(buffer);
-
- snprintf(buffer, SIZE,
- "Since Boot: Submissions: %8" PRId64
- " Accepted: %8" PRId64 "\n",
- mItemsSubmitted, mItemsFinalized);
- result.append(buffer);
- snprintf(buffer, SIZE,
- "Records Discarded: %8" PRId64
- " (by Count: %" PRId64 " by Expiration: %" PRId64 ")\n",
- mItemsDiscarded, mItemsDiscardedCount, mItemsDiscardedExpire);
- result.append(buffer);
- if (ts_since != 0) {
- snprintf(buffer, SIZE,
- "Emitting Queue entries more recent than: %" PRId64 "\n",
- (int64_t) ts_since);
- result.append(buffer);
- }
-}
-
-// the recent, detailed queues
-void MediaAnalyticsService::dumpRecent(String8 &result, nsecs_t ts_since, const char * only)
-{
- const size_t SIZE = 512;
- char buffer[SIZE];
-
- if (only != NULL && *only == '\0') {
- only = NULL;
- }
-
- // show the recently recorded records
- snprintf(buffer, sizeof(buffer), "\nFinalized Metrics (oldest first):\n");
- result.append(buffer);
- result.append(this->dumpQueue(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
-}
-
-// caller has locked mLock...
-String8 MediaAnalyticsService::dumpQueue() {
- return dumpQueue((nsecs_t) 0, NULL);
-}
-
-String8 MediaAnalyticsService::dumpQueue(nsecs_t ts_since, const char * only) {
- String8 result;
- int slot = 0;
-
- if (mItems.empty()) {
- result.append("empty\n");
- } else {
- List<MediaAnalyticsItem *>::iterator it = mItems.begin();
- for (; it != mItems.end(); it++) {
- nsecs_t when = (*it)->getTimestamp();
- if (when < ts_since) {
- continue;
- }
- if (only != NULL &&
- strcmp(only, (*it)->getKey().c_str()) != 0) {
- ALOGV("Omit '%s', it's not '%s'", (*it)->getKey().c_str(), only);
- continue;
- }
- std::string entry = (*it)->toString(mDumpProto);
- result.appendFormat("%5d: %s\n", slot, entry.c_str());
- slot++;
- }
- }
-
- return result;
-}
-
-//
-// Our Cheap in-core, non-persistent records management.
-
-
-// we hold mLock when we get here
-// if item != NULL, it's the item we just inserted
-// true == more items eligible to be recovered
-bool MediaAnalyticsService::expirations_l(MediaAnalyticsItem *item)
-{
- bool more = false;
- int handled = 0;
-
- // keep removing old records the front until we're in-bounds (count)
- // since we invoke this with each insertion, it should be 0/1 iterations.
- if (mMaxRecords > 0) {
- while (mItems.size() > (size_t) mMaxRecords) {
- MediaAnalyticsItem * oitem = *(mItems.begin());
- if (oitem == item) {
- break;
- }
- if (handled >= mMaxRecordsExpiredAtOnce) {
- // unlikely in this loop
- more = true;
- break;
- }
- handled++;
- mItems.erase(mItems.begin());
- delete oitem;
- mItemsDiscarded++;
- mItemsDiscardedCount++;
- }
- }
-
- // keep removing old records the front until we're in-bounds (age)
- // limited to mMaxRecordsExpiredAtOnce items per invocation.
- if (mMaxRecordAgeNs > 0) {
- nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
- while (mItems.size() > 0) {
- MediaAnalyticsItem * oitem = *(mItems.begin());
- nsecs_t when = oitem->getTimestamp();
- if (oitem == item) {
- break;
- }
- // careful about timejumps too
- if ((now > when) && (now-when) <= mMaxRecordAgeNs) {
- // this (and the rest) are recent enough to keep
- break;
- }
- if (handled >= mMaxRecordsExpiredAtOnce) {
- // this represents "one too many"; tell caller there are
- // more to be reclaimed.
- more = true;
- break;
- }
- handled++;
- mItems.erase(mItems.begin());
- delete oitem;
- mItemsDiscarded++;
- mItemsDiscardedExpire++;
- }
- }
-
- // we only indicate whether there's more to clean;
- // caller chooses whether to schedule further cleanup.
- return more;
-}
-
-// process expirations in bite sized chunks, allowing new insertions through
-// runs in a pthread specifically started for this (which then exits)
-bool MediaAnalyticsService::processExpirations()
-{
- bool more;
- do {
- sleep(1);
- {
- Mutex::Autolock _l(mLock);
- more = expirations_l(NULL);
- if (!more) {
- break;
- }
- }
- } while (more);
- return true; // value is for std::future thread synchronization
-}
-
-// insert appropriately into queue
-void MediaAnalyticsService::saveItem(MediaAnalyticsItem * item)
-{
-
- Mutex::Autolock _l(mLock);
- // mutex between insertion and dumping the contents
-
- // we want to dump 'in FIFO order', so insert at the end
- mItems.push_back(item);
-
- // clean old stuff from the queue
- bool more = expirations_l(item);
-
- // consider scheduling some asynchronous cleaning, if not running
- if (more) {
- if (!mExpireFuture.valid()
- || mExpireFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
-
- mExpireFuture = std::async(std::launch::async, [this]()
- {return this->processExpirations();});
- }
- }
-}
-
-static std::string allowedKeys[] =
-{
- "audiopolicy",
- "audiorecord",
- "audiothread",
- "audiotrack",
- "codec",
- "extractor",
- "nuplayer",
-};
-
-static const int nAllowedKeys = sizeof(allowedKeys) / sizeof(allowedKeys[0]);
-
-// are the contents good
-bool MediaAnalyticsService::contentValid(MediaAnalyticsItem *item, bool isTrusted) {
-
- // untrusted uids can only send us a limited set of keys
- if (isTrusted == false) {
- // restrict to a specific set of keys
- std::string key = item->getKey();
-
- size_t i;
- for(i = 0; i < nAllowedKeys; i++) {
- if (key == allowedKeys[i]) {
- break;
- }
- }
- if (i == nAllowedKeys) {
- ALOGD("Ignoring (key): %s", item->toString().c_str());
- return false;
- }
- }
-
- // internal consistency
-
- return true;
-}
-
-// are we rate limited, normally false
-bool MediaAnalyticsService::rateLimited(MediaAnalyticsItem *) {
-
- return false;
-}
-
-// how long we hold package info before we re-fetch it
-#define PKG_EXPIRATION_NS (30*60*1000000000ll) // 30 minutes, in nsecs
-
-// give me the package name, perhaps going to find it
-// manages its own mutex operations internally
-void MediaAnalyticsService::setPkgInfo(MediaAnalyticsItem *item, uid_t uid, bool setName, bool setVersion)
-{
- ALOGV("asking for packagename to go with uid=%d", uid);
-
- if (!setName && !setVersion) {
- // setting nothing? strange
- return;
- }
-
- nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
- struct UidToPkgMap mapping;
- mapping.uid = (uid_t)(-1);
-
- {
- Mutex::Autolock _l(mLock_mappings);
- int i = mPkgMappings.indexOfKey(uid);
- if (i >= 0) {
- mapping = mPkgMappings.valueAt(i);
- ALOGV("Expiration? uid %d expiration %" PRId64 " now %" PRId64,
- uid, mapping.expiration, now);
- if (mapping.expiration <= now) {
- // purge the stale entry and fall into re-fetching
- ALOGV("entry for uid %d expired, now= %" PRId64 "", uid, now);
- mPkgMappings.removeItemsAt(i);
- mapping.uid = (uid_t)(-1);
- }
- }
- }
-
- // if we did not find it
- if (mapping.uid == (uid_t)(-1)) {
- std::string pkg;
- std::string installer = "";
- int64_t versionCode = 0;
-
- struct passwd *pw = getpwuid(uid);
- if (pw) {
- pkg = pw->pw_name;
- }
-
- // find the proper value
-
- sp<IBinder> binder = NULL;
- sp<IServiceManager> sm = defaultServiceManager();
- if (sm == NULL) {
- ALOGE("defaultServiceManager failed");
- } else {
- binder = sm->getService(String16("package_native"));
- if (binder == NULL) {
- ALOGE("getService package_native failed");
- }
- }
-
- if (binder != NULL) {
- sp<content::pm::IPackageManagerNative> package_mgr =
- interface_cast<content::pm::IPackageManagerNative>(binder);
- binder::Status status;
-
- std::vector<int> uids;
- std::vector<std::string> names;
-
- uids.push_back(uid);
-
- status = package_mgr->getNamesForUids(uids, &names);
- if (!status.isOk()) {
- ALOGE("package_native::getNamesForUids failed: %s",
- 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()) {
- // 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 {
- String16 pkgName16(pkg.c_str());
- status = package_mgr->getInstallerForPackage(pkgName16, &installer);
- if (!status.isOk()) {
- ALOGE("package_native::getInstallerForPackage failed: %s",
- 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("package_native::getVersionCodeForPackage failed: %s",
- status.exceptionMessage().c_str());
- }
- }
-
-
- ALOGV("package '%s' installed by '%s' versioncode %" PRId64 " / %" PRIx64,
- pkg.c_str(), installer.c_str(), versionCode, 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); do not report
- pkg = "";
- versionCode = 0;
- } else {
- // unknown installer; do not report
- pkg = "";
- versionCode = 0;
- }
- }
- }
-
- // add it to the map, to save a subsequent lookup
- if (!pkg.empty()) {
- Mutex::Autolock _l(mLock_mappings);
- ALOGV("Adding uid %d pkg '%s'", uid, pkg.c_str());
- ssize_t i = mPkgMappings.indexOfKey(uid);
- if (i < 0) {
- mapping.uid = uid;
- mapping.pkg = pkg;
- mapping.installer = installer.c_str();
- mapping.versionCode = versionCode;
- mapping.expiration = now + PKG_EXPIRATION_NS;
- ALOGV("expiration for uid %d set to %" PRId64 "", uid, mapping.expiration);
-
- mPkgMappings.add(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/mediaanalytics/MediaAnalyticsService.h b/services/mediaanalytics/MediaAnalyticsService.h
deleted file mode 100644
index 6c9cbaa..0000000
--- a/services/mediaanalytics/MediaAnalyticsService.h
+++ /dev/null
@@ -1,122 +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.
- */
-
-
-#ifndef ANDROID_MEDIAANALYTICSSERVICE_H
-#define ANDROID_MEDIAANALYTICSSERVICE_H
-
-#include <arpa/inet.h>
-
-#include <utils/threads.h>
-#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <utils/String8.h>
-#include <utils/List.h>
-
-#include <future>
-
-#include <media/IMediaAnalyticsService.h>
-
-namespace android {
-
-class MediaAnalyticsService : public BnMediaAnalyticsService
-{
-
- public:
-
- // on this side, caller surrenders ownership
- virtual int64_t submit(MediaAnalyticsItem *item, bool forcenew);
-
- static void instantiate();
- virtual status_t dump(int fd, const Vector<String16>& args);
-
- MediaAnalyticsService();
- virtual ~MediaAnalyticsService();
-
- bool processExpirations();
-
- private:
- MediaAnalyticsItem::SessionID_t generateUniqueSessionID();
-
- // statistics about our analytics
- int64_t mItemsSubmitted;
- int64_t mItemsFinalized;
- int64_t mItemsDiscarded;
- int64_t mItemsDiscardedExpire;
- int64_t mItemsDiscardedCount;
- MediaAnalyticsItem::SessionID_t mLastSessionID;
-
- // partitioned a bit so we don't over serialize
- mutable Mutex mLock;
- mutable Mutex mLock_ids;
- mutable Mutex mLock_mappings;
-
- // limit how many records we'll retain
- // by count (in each queue (open, finalized))
- int32_t mMaxRecords;
- // by time (none older than this long agan
- nsecs_t mMaxRecordAgeNs;
- // max to expire per expirations_l() invocation
- int32_t mMaxRecordsExpiredAtOnce;
- //
- // # of sets of summaries
- int32_t mMaxRecordSets;
- // nsecs until we start a new record set
- nsecs_t mNewSetInterval;
-
- // input validation after arrival from client
- bool contentValid(MediaAnalyticsItem *item, bool isTrusted);
- bool rateLimited(MediaAnalyticsItem *);
-
- // (oldest at front) so it prints nicely for dumpsys
- List<MediaAnalyticsItem *> mItems;
- void saveItem(MediaAnalyticsItem *);
-
- bool expirations_l(MediaAnalyticsItem *);
- std::future<bool> mExpireFuture;
-
- // support for generating output
- int mDumpProto;
- int mDumpProtoDefault;
- String8 dumpQueue();
- String8 dumpQueue(nsecs_t, const char *only);
-
- void dumpHeaders(String8 &result, nsecs_t ts_since);
- void dumpSummaries(String8 &result, nsecs_t ts_since, const char * only);
- void dumpRecent(String8 &result, nsecs_t ts_since, const char * only);
-
- // mapping uids to package names
- struct UidToPkgMap {
- uid_t uid;
- std::string pkg;
- std::string installer;
- int64_t versionCode;
- nsecs_t expiration;
- };
-
- KeyedVector<uid_t,struct UidToPkgMap> mPkgMappings;
- void setPkgInfo(MediaAnalyticsItem *item, uid_t uid, bool setName, bool setVersion);
-
-};
-
-// hook to send things off to the statsd subsystem
-extern bool dump2Statsd(MediaAnalyticsItem *item);
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_MEDIAANALYTICSSERVICE_H
diff --git a/services/mediaanalytics/OWNERS b/services/mediaanalytics/OWNERS
deleted file mode 100644
index 9af258b..0000000
--- a/services/mediaanalytics/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-essick@google.com
diff --git a/services/mediaanalytics/iface_statsd.h b/services/mediaanalytics/iface_statsd.h
deleted file mode 100644
index f85d303..0000000
--- a/services/mediaanalytics/iface_statsd.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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.
- */
-
-namespace android {
-
-extern bool enabled_statsd;
-
-// component specific dumpers
-extern bool statsd_audiopolicy(MediaAnalyticsItem *);
-extern bool statsd_audiorecord(MediaAnalyticsItem *);
-extern bool statsd_audiothread(MediaAnalyticsItem *);
-extern bool statsd_audiotrack(MediaAnalyticsItem *);
-extern bool statsd_codec(MediaAnalyticsItem *);
-extern bool statsd_extractor(MediaAnalyticsItem *);
-extern bool statsd_nuplayer(MediaAnalyticsItem *);
-extern bool statsd_recorder(MediaAnalyticsItem *);
-
-extern bool statsd_mediadrm(MediaAnalyticsItem *);
-extern bool statsd_widevineCDM(MediaAnalyticsItem *);
-
-} // namespace android
diff --git a/services/mediacodec/Android.bp b/services/mediacodec/Android.bp
index e319425..5811068 100644
--- a/services/mediacodec/Android.bp
+++ b/services/mediacodec/Android.bp
@@ -58,5 +58,8 @@
src: "seccomp_policy/mediacodec-x86.policy",
},
},
- required: ["crash_dump.policy"],
+ required: [
+ "crash_dump.policy",
+ "code_coverage.policy",
+ ],
}
diff --git a/services/mediacodec/Android.mk b/services/mediacodec/Android.mk
index cdd9d0c..88a79e7 100644
--- a/services/mediacodec/Android.mk
+++ b/services/mediacodec/Android.mk
@@ -70,7 +70,7 @@
LOCAL_MODULE := mediacodec.policy
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT)/etc/seccomp_policy
-LOCAL_REQUIRED_MODULES := crash_dump.policy
+LOCAL_REQUIRED_MODULES := crash_dump.policy code_coverage.policy
# mediacodec runs in 32-bit combatibility mode. For 64 bit architectures,
# use the 32 bit policy
ifdef TARGET_2ND_ARCH
diff --git a/services/mediacodec/registrant/Android.bp b/services/mediacodec/registrant/Android.bp
index fa5bc4a..ad03e68 100644
--- a/services/mediacodec/registrant/Android.bp
+++ b/services/mediacodec/registrant/Android.bp
@@ -9,12 +9,11 @@
"libmedia_headers",
],
+ defaults: [
+ "libcodec2-hidl-defaults",
+ ],
shared_libs: [
- "android.hardware.media.c2@1.0",
"libbase",
- "libcodec2_hidl@1.0",
- "libcodec2_vndk",
- "libutils",
],
// Codecs
diff --git a/services/mediacodec/registrant/CodecServiceRegistrant.cpp b/services/mediacodec/registrant/CodecServiceRegistrant.cpp
index 706ebee..58db801e 100644
--- a/services/mediacodec/registrant/CodecServiceRegistrant.cpp
+++ b/services/mediacodec/registrant/CodecServiceRegistrant.cpp
@@ -20,11 +20,11 @@
#include <android-base/logging.h>
#include <C2PlatformSupport.h>
-#include <codec2/hidl/1.0/ComponentStore.h>
+#include <codec2/hidl/1.1/ComponentStore.h>
#include <media/CodecServiceRegistrant.h>
extern "C" void RegisterCodecServices() {
- using namespace ::android::hardware::media::c2::V1_0;
+ using namespace ::android::hardware::media::c2::V1_1;
LOG(INFO) << "Creating software Codec2 service...";
android::sp<IComponentStore> store =
new utils::ComponentStore(
diff --git a/services/mediacodec/seccomp_policy/mediacodec-arm.policy b/services/mediacodec/seccomp_policy/mediacodec-arm.policy
index 3870a11..835f8bb 100644
--- a/services/mediacodec/seccomp_policy/mediacodec-arm.policy
+++ b/services/mediacodec/seccomp_policy/mediacodec-arm.policy
@@ -59,3 +59,5 @@
getrandom: 1
@include /system/etc/seccomp_policy/crash_dump.arm.policy
+
+@include /system/etc/seccomp_policy/code_coverage.arm.policy
diff --git a/services/mediacodec/seccomp_policy/mediacodec-x86.policy b/services/mediacodec/seccomp_policy/mediacodec-x86.policy
index d9c4045..a9d32d6 100644
--- a/services/mediacodec/seccomp_policy/mediacodec-x86.policy
+++ b/services/mediacodec/seccomp_policy/mediacodec-x86.policy
@@ -69,3 +69,4 @@
gettid: 1
@include /system/etc/seccomp_policy/crash_dump.x86.policy
+@include /system/etc/seccomp_policy/code_coverage.x86.policy
diff --git a/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy b/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy
index 9042cd7..93b4852 100644
--- a/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy
+++ b/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy
@@ -84,3 +84,5 @@
getgid32: 1
getegid32: 1
getgroups32: 1
+
+@include /system/etc/seccomp_policy/code_coverage.arm.policy
diff --git a/services/mediacodec/seccomp_policy/mediaswcodec-arm64.policy b/services/mediacodec/seccomp_policy/mediaswcodec-arm64.policy
index 4faf8b2..bb05770 100644
--- a/services/mediacodec/seccomp_policy/mediaswcodec-arm64.policy
+++ b/services/mediacodec/seccomp_policy/mediaswcodec-arm64.policy
@@ -79,3 +79,4 @@
getegid: 1
getgroups: 1
+@include /system/etc/seccomp_policy/code_coverage.arm64.policy
diff --git a/services/mediadrm/Android.mk b/services/mediadrm/Android.mk
deleted file mode 100644
index 72d42ae..0000000
--- a/services/mediadrm/Android.mk
+++ /dev/null
@@ -1,53 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- MediaDrmService.cpp \
- main_mediadrmserver.cpp
-
-LOCAL_HEADER_LIBRARIES:= \
- libmedia_headers \
- libmediadrm_headers
-
-LOCAL_SHARED_LIBRARIES:= \
- libbinder \
- liblog \
- libmedia \
- libmediadrm \
- libutils \
- libhidlbase \
- libhidlmemory \
- android.hardware.drm@1.0 \
- android.hardware.drm@1.1 \
- android.hardware.drm@1.2
-
-LOCAL_CFLAGS += -Wall -Wextra -Werror
-
-LOCAL_MODULE:= mediadrmserver
-
-# TODO: Some legacy DRM plugins only support 32-bit. They need to be migrated to
-# 64-bit. (b/18948909) Once all of a device's legacy DRM plugins support 64-bit,
-# that device can turn on TARGET_ENABLE_MEDIADRM_64 to build this service as
-# 64-bit.
-ifneq ($(TARGET_ENABLE_MEDIADRM_64), true)
-LOCAL_32_BIT_ONLY := true
-endif
-
-LOCAL_INIT_RC := mediadrmserver.rc
-
-include $(BUILD_EXECUTABLE)
diff --git a/services/mediadrm/MediaDrmService.cpp b/services/mediadrm/MediaDrmService.cpp
deleted file mode 100644
index 5afd079..0000000
--- a/services/mediadrm/MediaDrmService.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
-**
-** Copyright 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.
-*/
-
-// Proxy for media player implementations
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "MediaDrmService"
-
-#include "MediaDrmService.h"
-#include <binder/IServiceManager.h>
-#include <utils/Log.h>
-
-#include <mediadrm/CryptoHal.h>
-#include <mediadrm/DrmHal.h>
-
-namespace android {
-
-void MediaDrmService::instantiate() {
- defaultServiceManager()->addService(
- String16("media.drm"), new MediaDrmService());
-}
-
-sp<ICrypto> MediaDrmService::makeCrypto() {
- return new CryptoHal;
-}
-
-sp<IDrm> MediaDrmService::makeDrm() {
- return new DrmHal;
-}
-
-} // namespace android
diff --git a/services/mediadrm/MediaDrmService.h b/services/mediadrm/MediaDrmService.h
deleted file mode 100644
index 3607201..0000000
--- a/services/mediadrm/MediaDrmService.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
-**
-** Copyright 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_MEDIADRMSERVICE_H
-#define ANDROID_MEDIADRMSERVICE_H
-
-#include <arpa/inet.h>
-
-#include <utils/threads.h>
-
-#include <media/Metadata.h>
-#include <media/stagefright/foundation/ABase.h>
-#include <mediadrm/IMediaDrmService.h>
-
-namespace android {
-
-class MediaDrmService : public BnMediaDrmService
-{
-public:
- static void instantiate();
-
- // IMediaDrmService interface
- virtual sp<ICrypto> makeCrypto();
- virtual sp<IDrm> makeDrm();
-private:
- MediaDrmService() {}
- virtual ~MediaDrmService() {}
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_MEDIADRMSERVICE_H
diff --git a/services/mediadrm/OWNERS b/services/mediadrm/OWNERS
deleted file mode 100644
index 6d3b533..0000000
--- a/services/mediadrm/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-jtinker@google.com
-marcone@google.com
diff --git a/services/mediadrm/main_mediadrmserver.cpp b/services/mediadrm/main_mediadrmserver.cpp
deleted file mode 100644
index b767b8c..0000000
--- a/services/mediadrm/main_mediadrmserver.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
-**
-** Copyright 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.
-*/
-
-#define LOG_TAG "mediaserver"
-//#define LOG_NDEBUG 0
-
-#include <fcntl.h>
-#include <sys/prctl.h>
-#include <sys/wait.h>
-#include <binder/IPCThreadState.h>
-#include <binder/ProcessState.h>
-#include <binder/IServiceManager.h>
-#include <cutils/properties.h>
-#include <utils/Log.h>
-#include "MediaDrmService.h"
-
-using namespace android;
-
-int main()
-{
- signal(SIGPIPE, SIG_IGN);
-
- sp<ProcessState> proc(ProcessState::self());
- sp<IServiceManager> sm = defaultServiceManager();
- ALOGI("ServiceManager: %p", sm.get());
- MediaDrmService::instantiate();
- ProcessState::self()->startThreadPool();
- IPCThreadState::self()->joinThreadPool();
-}
diff --git a/services/mediadrm/mediadrmserver.rc b/services/mediadrm/mediadrmserver.rc
deleted file mode 100644
index 359c2cf..0000000
--- a/services/mediadrm/mediadrmserver.rc
+++ /dev/null
@@ -1,6 +0,0 @@
-service mediadrm /system/bin/mediadrmserver
- class main
- user media
- group mediadrm drmrpc
- ioprio rt 4
- writepid /dev/cpuset/foreground/tasks
diff --git a/services/mediaextractor/Android.bp b/services/mediaextractor/Android.bp
index e906500..816a6b1 100644
--- a/services/mediaextractor/Android.bp
+++ b/services/mediaextractor/Android.bp
@@ -13,6 +13,7 @@
"libstagefright",
"libbinder",
"libutils",
+ "liblog",
],
}
@@ -67,6 +68,9 @@
src: "seccomp_policy/mediaextractor-x86_64.policy",
},
},
- required: ["crash_dump.policy"],
+ required: [
+ "crash_dump.policy",
+ "code_coverage.policy",
+ ],
}
diff --git a/services/mediaextractor/MediaExtractorService.cpp b/services/mediaextractor/MediaExtractorService.cpp
index 6239fb2..a6cd224 100644
--- a/services/mediaextractor/MediaExtractorService.cpp
+++ b/services/mediaextractor/MediaExtractorService.cpp
@@ -29,48 +29,55 @@
namespace android {
-MediaExtractorService::MediaExtractorService()
- : BnMediaExtractorService() {
+MediaExtractorService::MediaExtractorService() {
MediaExtractorFactory::LoadExtractors();
}
-sp<IMediaExtractor> MediaExtractorService::makeExtractor(
- const sp<IDataSource> &remoteSource, const char *mime) {
- ALOGV("@@@ MediaExtractorService::makeExtractor for %s", mime);
+MediaExtractorService::~MediaExtractorService() {
+ ALOGE("should not be in ~MediaExtractorService");
+}
+
+::android::binder::Status MediaExtractorService::makeExtractor(
+ const ::android::sp<::android::IDataSource>& remoteSource,
+ const ::std::unique_ptr< ::std::string> &mime,
+ ::android::sp<::android::IMediaExtractor>* _aidl_return) {
+ ALOGV("@@@ MediaExtractorService::makeExtractor for %s", mime.get()->c_str());
sp<DataSource> localSource = CreateDataSourceFromIDataSource(remoteSource);
- sp<IMediaExtractor> extractor = MediaExtractorFactory::CreateFromService(localSource, mime);
+ sp<IMediaExtractor> extractor = MediaExtractorFactory::CreateFromService(
+ localSource,
+ mime.get() ? mime.get()->c_str() : nullptr);
ALOGV("extractor service created %p (%s)",
extractor.get(),
extractor == nullptr ? "" : extractor->name());
if (extractor != nullptr) {
- registerMediaExtractor(extractor, localSource, mime);
- return extractor;
+ registerMediaExtractor(extractor, localSource, mime.get() ? mime.get()->c_str() : nullptr);
}
- return nullptr;
+ *_aidl_return = extractor;
+ return binder::Status::ok();
}
-sp<IDataSource> MediaExtractorService::makeIDataSource(int fd, int64_t offset, int64_t length)
-{
- sp<DataSource> source = DataSourceFactory::getInstance()->CreateFromFd(fd, offset, length);
- return CreateIDataSourceFromDataSource(source);
+::android::binder::Status MediaExtractorService::makeIDataSource(
+ base::unique_fd fd,
+ int64_t offset,
+ int64_t length,
+ ::android::sp<::android::IDataSource>* _aidl_return) {
+ sp<DataSource> source = DataSourceFactory::getInstance()->CreateFromFd(fd.release(), offset, length);
+ *_aidl_return = CreateIDataSourceFromDataSource(source);
+ return binder::Status::ok();
}
-std::unordered_set<std::string> MediaExtractorService::getSupportedTypes() {
- return MediaExtractorFactory::getSupportedTypes();
+::android::binder::Status MediaExtractorService::getSupportedTypes(
+ ::std::vector<::std::string>* _aidl_return) {
+ *_aidl_return = MediaExtractorFactory::getSupportedTypes();
+ return binder::Status::ok();
}
status_t MediaExtractorService::dump(int fd, const Vector<String16>& args) {
return MediaExtractorFactory::dump(fd, args) || dumpExtractors(fd, args);
}
-status_t MediaExtractorService::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags)
-{
- return BnMediaExtractorService::onTransact(code, data, reply, flags);
-}
-
} // namespace android
diff --git a/services/mediaextractor/MediaExtractorService.h b/services/mediaextractor/MediaExtractorService.h
index c9cebcf..1b40bf9 100644
--- a/services/mediaextractor/MediaExtractorService.h
+++ b/services/mediaextractor/MediaExtractorService.h
@@ -18,31 +18,33 @@
#define ANDROID_MEDIA_EXTRACTOR_SERVICE_H
#include <binder/BinderService.h>
-#include <media/IMediaExtractorService.h>
-#include <media/IMediaExtractor.h>
+#include <android/BnMediaExtractorService.h>
+#include <android/IMediaExtractor.h>
namespace android {
class MediaExtractorService : public BinderService<MediaExtractorService>, public BnMediaExtractorService
{
- friend class BinderService<MediaExtractorService>; // for MediaExtractorService()
public:
MediaExtractorService();
- virtual ~MediaExtractorService() { }
- virtual void onFirstRef() { }
+ virtual ~MediaExtractorService();
static const char* getServiceName() { return "media.extractor"; }
- virtual sp<IMediaExtractor> makeExtractor(const sp<IDataSource> &source, const char *mime);
+ virtual ::android::binder::Status makeExtractor(
+ const ::android::sp<::android::IDataSource>& source,
+ const ::std::unique_ptr< ::std::string> &mime,
+ ::android::sp<::android::IMediaExtractor>* _aidl_return);
- virtual sp<IDataSource> makeIDataSource(int fd, int64_t offset, int64_t length);
+ virtual ::android::binder::Status makeIDataSource(
+ base::unique_fd fd,
+ int64_t offset,
+ int64_t length,
+ ::android::sp<::android::IDataSource>* _aidl_return);
- virtual std::unordered_set<std::string> getSupportedTypes();
+ virtual ::android::binder::Status getSupportedTypes(::std::vector<::std::string>* _aidl_return);
- virtual status_t dump(int fd, const Vector<String16>& args);
-
- 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);
private:
Mutex mLock;
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy b/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy
index 964acf4..38f9be6 100644
--- a/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy
@@ -50,3 +50,4 @@
_llseek: 1
@include /system/etc/seccomp_policy/crash_dump.arm.policy
+@include /system/etc/seccomp_policy/code_coverage.arm.policy
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy b/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy
index e6c676c..8fd8787 100644
--- a/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy
@@ -44,3 +44,4 @@
sched_yield: 1
@include /system/etc/seccomp_policy/crash_dump.arm64.policy
+@include /system/etc/seccomp_policy/code_coverage.arm64.policy
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy b/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy
index 56ad8df..05915d1 100644
--- a/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy
@@ -57,3 +57,4 @@
gettid: 1
@include /system/etc/seccomp_policy/crash_dump.x86.policy
+@include /system/etc/seccomp_policy/code_coverage.x86.policy
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-x86_64.policy b/services/mediaextractor/seccomp_policy/mediaextractor-x86_64.policy
index 607a03e..e6a55d0 100644
--- a/services/mediaextractor/seccomp_policy/mediaextractor-x86_64.policy
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-x86_64.policy
@@ -51,3 +51,4 @@
gettid: 1
@include /system/etc/seccomp_policy/crash_dump.x86_64.policy
+@include /system/etc/seccomp_policy/code_coverage.x86_64.policy
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..4711bb6
--- /dev/null
+++ b/services/mediametrics/AnalyticsState.h
@@ -0,0 +1,113 @@
+/*
+ * 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 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
new file mode 100644
index 0000000..20f346c
--- /dev/null
+++ b/services/mediametrics/Android.bp
@@ -0,0 +1,75 @@
+// Media Statistics service
+//
+
+cc_binary {
+ name: "mediametrics",
+
+ srcs: [
+ "main_mediametrics.cpp",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "liblog",
+ "libmediametricsservice",
+ "libmediautils",
+ "libutils",
+ ],
+
+ init_rc: [
+ "mediametrics.rc",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+}
+
+cc_library_shared {
+ name: "libmediametricsservice",
+
+ srcs: [
+ "AudioAnalytics.cpp",
+ "iface_statsd.cpp",
+ "MediaMetricsService.cpp",
+ "statsd_audiopolicy.cpp",
+ "statsd_audiorecord.cpp",
+ "statsd_audiothread.cpp",
+ "statsd_audiotrack.cpp",
+ "statsd_codec.cpp",
+ "statsd_drm.cpp",
+ "statsd_extractor.cpp",
+ "statsd_nuplayer.cpp",
+ "statsd_recorder.cpp",
+ ],
+
+ proto: {
+ type: "lite",
+ },
+
+ shared_libs: [
+ "libbinder",
+ "liblog",
+ "libmediametrics",
+ "libmediautils",
+ "libprotobuf-cpp-lite",
+ "libstatslog",
+ "libutils",
+ ],
+
+ static_libs: [
+ "libplatformprotos",
+ ],
+
+ include_dirs: [
+ "system/media/audio_utils/include",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+}
diff --git a/services/mediametrics/AudioAnalytics.cpp b/services/mediametrics/AudioAnalytics.cpp
new file mode 100644
index 0000000..f7ee051
--- /dev/null
+++ b/services/mediametrics/AudioAnalytics.cpp
@@ -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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AudioAnalytics"
+#include <utils/Log.h>
+
+#include "AudioAnalytics.h"
+
+#include <audio_utils/clock.h> // clock conversions
+
+namespace android::mediametrics {
+
+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(
+ "audio.flinger.event",
+ std::string("AudioFlinger"),
+ std::make_shared<AnalyticsActions::Function>(
+ [this](const std::shared_ptr<const android::mediametrics::Item> &){
+ ALOGW("Audioflinger() constructor event detected");
+ 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.
+ }));
+}
+
+AudioAnalytics::~AudioAnalytics()
+{
+ ALOGD("%s", __func__);
+}
+
+status_t AudioAnalytics::submit(
+ const std::shared_ptr<const mediametrics::Item>& item, bool isTrusted)
+{
+ if (!startsWith(item->getKey(), "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
+{
+ std::stringstream ss;
+ int32_t ll = lines;
+
+ if (ll > 0) {
+ auto [s, l] = mAnalyticsState->dump(ll);
+ ss << s;
+ ll -= l;
+ }
+ if (ll > 0) {
+ ss << "Prior audioserver state:\n";
+ --ll;
+ }
+ if (ll > 0) {
+ 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);
+ }
+}
+
+} // namespace android
diff --git a/services/mediametrics/AudioAnalytics.h b/services/mediametrics/AudioAnalytics.h
new file mode 100644
index 0000000..be885ec
--- /dev/null
+++ b/services/mediametrics/AudioAnalytics.h
@@ -0,0 +1,85 @@
+/*
+ * 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 "AnalyticsActions.h"
+#include "AnalyticsState.h"
+#include "Wrap.h"
+
+namespace android::mediametrics {
+
+class AudioAnalytics
+{
+public:
+ AudioAnalytics();
+ ~AudioAnalytics();
+
+ /**
+ * Returns success if AudioAnalytics recognizes item.
+ *
+ * AudioAnalytics requires the item key to start with "audio.".
+ *
+ * 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,
+ * 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 mediametrics::Item>& item, bool isTrusted);
+
+ /**
+ * 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;
+
+private:
+
+ /**
+ * 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);
+
+ // 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
new file mode 100644
index 0000000..3b95f7a
--- /dev/null
+++ b/services/mediametrics/MediaMetricsService.cpp
@@ -0,0 +1,481 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaMetricsService"
+#include <utils/Log.h>
+
+#include "MediaMetricsService.h"
+
+#include <pwd.h> //getpwuid
+
+#include <android/content/pm/IPackageManagerNative.h> // package info
+#include <audio_utils/clock.h> // clock conversions
+#include <binder/IPCThreadState.h> // get calling uid
+#include <cutils/properties.h> // for property_get
+#include <private/android_filesystem_config.h> // UID
+
+namespace android {
+
+using namespace mediametrics;
+
+// individual records kept in memory: age or count
+// age: <= 28 hours (1 1/6 days)
+// count: hard limit of # records
+// (0 for either of these disables that threshold)
+//
+static constexpr nsecs_t kMaxRecordAgeNs = 28 * 3600 * NANOS_PER_SECOND;
+// 2019/6: average daily per device is currently 375-ish;
+// setting this to 2000 is large enough to catch most devices
+// we'll lose some data on very very media-active devices, but only for
+// the gms collection; statsd will have already covered those for us.
+// This also retains enough information to help with bugreports
+static constexpr size_t kMaxRecords = 2000;
+
+// max we expire in a single call, to constrain how long we hold the
+// mutex, which also constrains how long a client might wait.
+static constexpr size_t kMaxExpiredAtOnce = 50;
+
+// TODO: need to look at tuning kMaxRecords and friends for low-memory devices
+
+/* static */
+nsecs_t MediaMetricsService::roundTime(nsecs_t timeNs)
+{
+ return (timeNs + NANOS_PER_SECOND / 2) / NANOS_PER_SECOND * NANOS_PER_SECOND;
+}
+
+/* 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(mediametrics::Item::PROTO_V1)
+{
+ ALOGD("%s", __func__);
+}
+
+MediaMetricsService::~MediaMetricsService()
+{
+ ALOGD("%s", __func__);
+ // the class destructor clears anyhow, but we enforce clearing items first.
+ mItemsDiscarded += mItems.size();
+ mItems.clear();
+}
+
+status_t MediaMetricsService::submitInternal(mediametrics::Item *item, bool release)
+{
+ // calling PID is 0 for one-way calls.
+ const pid_t pid = IPCThreadState::self()->getCallingPid();
+ const pid_t pid_given = item->getPid();
+ const uid_t uid = IPCThreadState::self()->getCallingUid();
+ const uid_t uid_given = item->getUid();
+
+ //ALOGD("%s: caller pid=%d uid=%d, item pid=%d uid=%d", __func__,
+ // (int)pid, (int)uid, (int) pid_given, (int)uid_given);
+
+ bool isTrusted;
+ switch (uid) {
+ case AID_AUDIOSERVER:
+ case AID_BLUETOOTH:
+ case AID_CAMERA:
+ case AID_DRM:
+ case AID_MEDIA:
+ case AID_MEDIA_CODEC:
+ case AID_MEDIA_EX:
+ case AID_MEDIA_DRM:
+ case AID_SYSTEM:
+ // trusted source, only override default values
+ isTrusted = true;
+ if (uid_given == (uid_t)-1) {
+ item->setUid(uid);
+ }
+ if (pid_given == (pid_t)-1) {
+ item->setPid(pid); // if one-way then this is 0.
+ }
+ break;
+ default:
+ isTrusted = false;
+ item->setPid(pid); // always use calling pid, if one-way then this is 0.
+ item->setUid(uid);
+ break;
+ }
+
+ // 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 "
+ "sanitized pkg version: %lld",
+ __func__,
+ uid_given, item->getUid(),
+ item->getPkgName().c_str(),
+ (long long)item->getPkgVersionCode());
+
+ mItemsSubmitted++;
+
+ // validate the record; we discard if we don't like it
+ if (isContentValid(item, isTrusted) == false) {
+ if (release) delete item;
+ return PERMISSION_DENIED;
+ }
+
+ // XXX: if we have a sessionid in the new record, look to make
+ // sure it doesn't appear in the finalized list.
+
+ if (item->count() == 0) {
+ ALOGV("%s: dropping empty record...", __func__);
+ if (release) delete item;
+ return BAD_VALUE;
+ }
+
+ if (!isTrusted || item->getTimestamp() == 0) {
+ // WestWorld logs two times for events: ElapsedRealTimeNs (BOOTTIME) and
+ // WallClockTimeNs (REALTIME). The new audio keys use BOOTTIME.
+ //
+ // 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);
+ item->setTimestamp(now);
+ }
+
+ // now attach either the item or its dup to a const shared pointer
+ std::shared_ptr<const mediametrics::Item> sitem(release ? item : item->dup());
+
+ (void)mAudioAnalytics.submit(sitem, isTrusted);
+
+ 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 MediaMetricsService::dump(int fd, const Vector<String16>& args)
+{
+ String8 result;
+
+ if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
+ result.appendFormat("Permission Denial: "
+ "can't dump MediaMetricsService from pid=%d, uid=%d\n",
+ IPCThreadState::self()->getCallingPid(),
+ IPCThreadState::self()->getCallingUid());
+ write(fd, result.string(), result.size());
+ return NO_ERROR;
+ }
+
+ // crack any parameters
+ const String16 protoOption("-proto");
+ int chosenProto = mDumpProtoDefault;
+ const String16 clearOption("-clear");
+ bool clear = false;
+ const String16 sinceOption("-since");
+ nsecs_t ts_since = 0;
+ const String16 helpOption("-help");
+ const String16 onlyOption("-only");
+ std::string only;
+ const int n = args.size();
+ for (int i = 0; i < n; i++) {
+ if (args[i] == clearOption) {
+ clear = true;
+ } else if (args[i] == protoOption) {
+ i++;
+ if (i < n) {
+ String8 value(args[i]);
+ int proto = mediametrics::Item::PROTO_V0;
+ char *endp;
+ const char *p = value.string();
+ proto = strtol(p, &endp, 10);
+ if (endp != p || *endp == '\0') {
+ if (proto < mediametrics::Item::PROTO_FIRST) {
+ proto = mediametrics::Item::PROTO_FIRST;
+ } else if (proto > mediametrics::Item::PROTO_LAST) {
+ proto = mediametrics::Item::PROTO_LAST;
+ }
+ chosenProto = proto;
+ } else {
+ result.append("unable to parse value for -proto\n\n");
+ }
+ } else {
+ result.append("missing value for -proto\n\n");
+ }
+ } else if (args[i] == sinceOption) {
+ i++;
+ if (i < n) {
+ String8 value(args[i]);
+ char *endp;
+ const char *p = value.string();
+ ts_since = strtoll(p, &endp, 10);
+ if (endp == p || *endp != '\0') {
+ ts_since = 0;
+ }
+ } else {
+ ts_since = 0;
+ }
+ // command line is milliseconds; internal units are nano-seconds
+ ts_since *= NANOS_PER_MILLISECOND;
+ } else if (args[i] == onlyOption) {
+ i++;
+ if (i < n) {
+ String8 value(args[i]);
+ only = value.string();
+ }
+ } else if (args[i] == helpOption) {
+ // TODO: consider function area dumping.
+ // dumpsys media.metrics audiotrack,codec
+ // or dumpsys media.metrics audiotrack codec
+
+ result.append("Recognized parameters:\n");
+ result.append("-help this help message\n");
+ result.append("-proto # dump using protocol #");
+ result.append("-clear clears out saved records\n");
+ result.append("-only X process records for component X\n");
+ result.append("-since X include records since X\n");
+ result.append(" (X is milliseconds since the UNIX epoch)\n");
+ write(fd, result.string(), result.size());
+ return NO_ERROR;
+ }
+ }
+
+ {
+ 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());
+
+ if (clear) {
+ mItemsDiscarded += mItems.size();
+ mItems.clear();
+ // shall we clear the summary data too?
+ }
+ // TODO: maybe consider a better way of dumping audio analytics info.
+ constexpr int32_t linesToDump = 1000;
+ 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());
+ return NO_ERROR;
+}
+
+// dump headers
+void MediaMetricsService::dumpHeaders_l(String8 &result, int dumpProto, nsecs_t ts_since)
+{
+ result.appendFormat("Protocol Version: %d\n", dumpProto);
+ if (mediametrics::Item::isEnabled()) {
+ result.append("Metrics gathering: enabled\n");
+ } else {
+ result.append("Metrics gathering: DISABLED via property\n");
+ }
+ result.appendFormat(
+ "Since Boot: Submissions: %lld Accepted: %lld\n",
+ (long long)mItemsSubmitted.load(), (long long)mItemsFinalized);
+ result.appendFormat(
+ "Records Discarded: %lld (by Count: %lld by Expiration: %lld)\n",
+ (long long)mItemsDiscarded, (long long)mItemsDiscardedCount,
+ (long long)mItemsDiscardedExpire);
+ if (ts_since != 0) {
+ result.appendFormat(
+ "Emitting Queue entries more recent than: %lld\n",
+ (long long)ts_since);
+ }
+}
+
+void MediaMetricsService::dumpRecent_l(
+ String8 &result, int dumpProto, 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);
+
+ // 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 MediaMetricsService::dumpQueue_l(String8 &result, int dumpProto) {
+ dumpQueue_l(result, dumpProto, (nsecs_t) 0, nullptr /* only */);
+}
+
+void MediaMetricsService::dumpQueue_l(
+ String8 &result, int dumpProto, nsecs_t ts_since, const char * only) {
+ int slot = 0;
+
+ if (mItems.empty()) {
+ result.append("empty\n");
+ } else {
+ for (const auto &item : mItems) {
+ nsecs_t when = item->getTimestamp();
+ if (when < ts_since) {
+ continue;
+ }
+ // TODO: Only should be a set<string>
+ if (only != nullptr &&
+ item->getKey() /* std::string */ != only) {
+ ALOGV("%s: omit '%s', it's not '%s'",
+ __func__, item->getKey().c_str(), only);
+ continue;
+ }
+ result.appendFormat("%5d: %s\n",
+ slot, item->toString(dumpProto).c_str());
+ slot++;
+ }
+ }
+}
+
+//
+// Our Cheap in-core, non-persistent records management.
+
+// if item != NULL, it's the item we just inserted
+// true == more items eligible to be recovered
+bool MediaMetricsService::expirations_l(const std::shared_ptr<const mediametrics::Item>& item)
+{
+ bool more = false;
+
+ // check queue size
+ size_t overlimit = 0;
+ if (mMaxRecords > 0 && mItems.size() > mMaxRecords) {
+ overlimit = mItems.size() - mMaxRecords;
+ if (overlimit > mMaxRecordsExpiredAtOnce) {
+ more = true;
+ overlimit = mMaxRecordsExpiredAtOnce;
+ }
+ }
+
+ // check queue times
+ size_t expired = 0;
+ if (!more && mMaxRecordAgeNs > 0) {
+ const nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
+ // we check one at a time, skip search would be more efficient.
+ size_t i = overlimit;
+ for (; i < mItems.size(); ++i) {
+ auto &oitem = mItems[i];
+ nsecs_t when = oitem->getTimestamp();
+ if (oitem.get() == item.get()) {
+ break;
+ }
+ if (now > when && (now - when) <= mMaxRecordAgeNs) {
+ break; // TODO: if we use BOOTTIME, should be monotonic.
+ }
+ if (i >= mMaxRecordsExpiredAtOnce) {
+ // this represents "one too many"; tell caller there are
+ // more to be reclaimed.
+ more = true;
+ break;
+ }
+ }
+ expired = i - overlimit;
+ }
+
+ if (const size_t toErase = overlimit + expired;
+ toErase > 0) {
+ mItemsDiscardedCount += overlimit;
+ mItemsDiscardedExpire += expired;
+ mItemsDiscarded += toErase;
+ mItems.erase(mItems.begin(), mItems.begin() + toErase); // erase from front
+ }
+ return more;
+}
+
+void MediaMetricsService::processExpirations()
+{
+ bool more;
+ do {
+ sleep(1);
+ std::lock_guard _l(mLock);
+ more = expirations_l(nullptr);
+ } while (more);
+}
+
+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.
+ mItems.emplace_back(item);
+ ++mItemsFinalized;
+ if (expirations_l(item)
+ && (!mExpireFuture.valid()
+ || mExpireFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready)) {
+ mExpireFuture = std::async(std::launch::async, [this] { processExpirations(); });
+ }
+}
+
+/* static */
+bool MediaMetricsService::isContentValid(const mediametrics::Item *item, bool isTrusted)
+{
+ if (isTrusted) return true;
+ // untrusted uids can only send us a limited set of keys
+ const std::string &key = item->getKey();
+ if (startsWith(key, "audio.")) return true;
+ for (const char *allowedKey : {
+ // legacy audio
+ "audiopolicy",
+ "audiorecord",
+ "audiothread",
+ "audiotrack",
+ // other media
+ "codec",
+ "extractor",
+ "nuplayer",
+ }) {
+ if (key == allowedKey) {
+ return true;
+ }
+ }
+ ALOGD("%s: invalid key: %s", __func__, item->toString().c_str());
+ return false;
+}
+
+// are we rate limited, normally false
+bool MediaMetricsService::isRateLimited(mediametrics::Item *) const
+{
+ return false;
+}
+
+} // namespace android
diff --git a/services/mediametrics/MediaMetricsService.h b/services/mediametrics/MediaMetricsService.h
new file mode 100644
index 0000000..74a114a
--- /dev/null
+++ b/services/mediametrics/MediaMetricsService.h
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <atomic>
+#include <deque>
+#include <future>
+#include <mutex>
+#include <unordered_map>
+
+// IMediaMetricsService must include Vector, String16, Errors
+#include <media/IMediaMetricsService.h>
+#include <mediautils/ServiceUtilities.h>
+#include <utils/String8.h>
+
+#include "AudioAnalytics.h"
+
+namespace android {
+
+class MediaMetricsService : public BnMediaMetricsService
+{
+public:
+ MediaMetricsService();
+ ~MediaMetricsService() override;
+
+ /**
+ * Submits the indicated record to the mediaanalytics service.
+ *
+ * \param item the item to submit.
+ * \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(mediametrics::Item *item) override {
+ return submitInternal(item, false /* release */);
+ }
+
+ status_t submitBuffer(const char *buffer, size_t length) override {
+ mediametrics::Item *item = new mediametrics::Item();
+ return item->readFromByteString(buffer, length)
+ ?: submitInternal(item, true /* release */);
+ }
+
+ status_t dump(int fd, const Vector<String16>& args) override;
+
+ static constexpr const char * const kServiceName = "media.metrics";
+
+ /**
+ * Rounds time to the nearest second.
+ */
+ 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(mediametrics::Item *item, bool release) override;
+
+private:
+ void processExpirations();
+ // input validation after arrival from client
+ 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 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);
+
+ // The following variables accessed without mLock
+
+ // limit how many records we'll retain
+ // by count (in each queue (open, finalized))
+ const size_t mMaxRecords;
+ // by time (none older than this)
+ const nsecs_t mMaxRecordAgeNs;
+ // max to expire per expirations_l() invocation
+ const size_t mMaxRecordsExpiredAtOnce;
+ const int mDumpProtoDefault;
+
+ 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;
+ // statistics about our analytics
+ int64_t mItemsFinalized = 0; // GUARDED_BY(mLock)
+ int64_t mItemsDiscarded = 0; // GUARDED_BY(mLock)
+ int64_t mItemsDiscardedExpire = 0; // GUARDED_BY(mLock)
+ int64_t mItemsDiscardedCount = 0; // GUARDED_BY(mLock)
+
+ // If we have a worker thread to garbage collect
+ std::future<void> mExpireFuture; // GUARDED_BY(mLock)
+
+ // 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 mediametrics::Item>> mItems; // GUARDED_BY(mLock)
+};
+
+} // namespace android
diff --git a/services/mediametrics/OWNERS b/services/mediametrics/OWNERS
new file mode 100644
index 0000000..e37a1f8
--- /dev/null
+++ b/services/mediametrics/OWNERS
@@ -0,0 +1,2 @@
+essick@google.com
+hunga@google.com
diff --git a/services/mediametrics/TEST_MAPPING b/services/mediametrics/TEST_MAPPING
new file mode 100644
index 0000000..01e9e46
--- /dev/null
+++ b/services/mediametrics/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+ "presubmit": [
+ {
+ "name": "mediametrics_tests"
+ },
+ {
+ "name": "CtsNativeMediaMetricsTestCases"
+ }
+ ]
+}
diff --git a/services/mediametrics/TimeMachine.h b/services/mediametrics/TimeMachine.h
new file mode 100644
index 0000000..4d24ce4
--- /dev/null
+++ b/services/mediametrics/TimeMachine.h
@@ -0,0 +1,518 @@
+/*
+ * 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 <any>
+#include <map>
+#include <sstream>
+#include <string>
+#include <variant>
+#include <vector>
+
+#include <media/MediaMetricsItem.h>
+#include <utils/Timers.h>
+
+namespace android::mediametrics {
+
+// define a way of printing the monostate
+inline std::ostream & operator<< (std::ostream& s,
+ std::monostate const& v __unused) {
+ s << "none_item";
+ 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>
+std::ostream & operator<< (std::ostream& s,
+ std::variant<T0, Ts...> const& v) {
+ std::visit([&s](auto && arg){ s << std::forward<decltype(arg)>(arg); }, v);
+ return s;
+}
+
+/**
+ * The TimeMachine is used to record timing changes of MediaAnalyticItem
+ * properties.
+ *
+ * Any URL that ends with '!' will have a time sequence that keeps duplicates.
+ *
+ * The TimeMachine is NOT thread safe.
+ */
+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.
+ class KeyHistory {
+ public:
+ template <typename T>
+ KeyHistory(T key, pid_t pid, uid_t uid, int64_t time)
+ : mKey(key)
+ , mPid(pid)
+ , mUid(uid)
+ , mCreationTime(time)
+ , mLastModificationTime(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;
+ }
+
+ template <typename T>
+ status_t getValue(const std::string &property, T* value, int64_t time = 0) const {
+ if (time == 0) time = systemTime(SYSTEM_TIME_BOOTTIME);
+ const auto tsptr = mPropertyMap.find(property);
+ if (tsptr == mPropertyMap.end()) return BAD_VALUE;
+ const auto& timeSequence = tsptr->second;
+ auto eptr = timeSequence.upper_bound(time);
+ if (eptr == timeSequence.begin()) return BAD_VALUE;
+ --eptr;
+ if (eptr == timeSequence.end()) return BAD_VALUE;
+ const T* vptr = std::get_if<T>(&eptr->second);
+ if (vptr == nullptr) return BAD_VALUE;
+ *value = *vptr;
+ return NO_ERROR;
+ }
+
+ template <typename T>
+ status_t getValue(const std::string &property, T defaultValue, int64_t time = 0) const {
+ T value;
+ return getValue(property, &value, time) != NO_ERROR ? defaultValue : value;
+ }
+
+ void putProp(
+ 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>
+ void putValue(const std::string &property,
+ T&& e, int64_t time = 0) {
+ if (time == 0) time = systemTime(SYSTEM_TIME_BOOTTIME);
+ mLastModificationTime = time;
+ auto& timeSequence = mPropertyMap[property];
+ Elem el{std::forward<T>(e)};
+ if (timeSequence.empty() // no elements
+ || property.back() == '!' // keep duplicates TODO: remove?
+ || timeSequence.rbegin()->second != el) { // value changed
+ timeSequence.emplace(time, std::move(el));
+ }
+ }
+
+ std::pair<std::string, int32_t> dump(int32_t lines, int64_t time) const {
+ std::stringstream ss;
+ int32_t ll = lines;
+ for (auto& tsPair : mPropertyMap) {
+ if (ll <= 0) break;
+ ss << dump(mKey, tsPair, time);
+ --ll;
+ }
+ return { ss.str(), lines - ll };
+ }
+
+ int64_t getLastModificationTime() const { return mLastModificationTime; }
+
+ private:
+ static std::string dump(
+ const std::string &key,
+ const std::pair<std::string /* prop */, PropertyHistory>& tsPair,
+ int64_t time) {
+ const auto timeSequence = tsPair.second;
+ auto eptr = timeSequence.lower_bound(time);
+ if (eptr == timeSequence.end()) {
+ return tsPair.first + "={};\n";
+ }
+ std::stringstream ss;
+ ss << key << "." << tsPair.first << "={";
+ do {
+ ss << eptr->first << ":" << eptr->second << ",";
+ } while (++eptr != timeSequence.end());
+ ss << "};\n";
+ return ss.str();
+ }
+
+ const std::string mKey;
+ const pid_t mPid __unused;
+ const uid_t mUid;
+ const int64_t mCreationTime __unused;
+
+ int64_t mLastModificationTime;
+ std::map<std::string /* property */, PropertyHistory> mPropertyMap;
+ };
+
+ using History = std::map<std::string /* key */, std::shared_ptr<KeyHistory>>;
+
+ static inline constexpr size_t kKeyLowWaterMark = 500;
+ static inline constexpr size_t kKeyHighWaterMark = 1000;
+
+ // Estimated max data space usage is 3KB * kKeyHighWaterMark.
+
+public:
+
+ TimeMachine() = default;
+ TimeMachine(size_t keyLowWaterMark, size_t keyHighWaterMark)
+ : mKeyLowWaterMark(keyLowWaterMark)
+ , mKeyHighWaterMark(keyHighWaterMark) {
+ LOG_ALWAYS_FATAL_IF(keyHighWaterMark <= keyLowWaterMark,
+ "%s: required that keyHighWaterMark:%zu > keyLowWaterMark:%zu",
+ __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 mediametrics::Item>& item, bool isTrusted = false) {
+ const int64_t time = item->getTimestamp();
+ const std::string &key = item->getKey();
+
+ std::shared_ptr<KeyHistory> keyHistory;
+ {
+ std::vector<std::any> garbage;
+ std::lock_guard lock(mLock);
+
+ auto it = mHistory.find(key);
+ if (it == mHistory.end()) {
+ if (!isTrusted) return PERMISSION_DENIED;
+
+ (void)gc_l(garbage);
+
+ // no keylock needed here as we are sole owner
+ // until placed on mHistory.
+ keyHistory = std::make_shared<KeyHistory>(
+ key, item->getPid(), item->getUid(), time);
+ mHistory[key] = keyHistory;
+ } else {
+ keyHistory = it->second;
+ }
+ }
+
+ // deferred contains remote properties (for other keys) to do later.
+ std::vector<const mediametrics::Item::Prop *> deferred;
+ {
+ // handle local properties
+ std::lock_guard lock(getLockForKey(key));
+ if (!isTrusted) {
+ status_t status = keyHistory->checkPermission(item->getUid());
+ if (status != NO_ERROR) return status;
+ }
+
+ for (const auto &prop : *item) {
+ const std::string &name = prop.getName();
+ if (name.size() == 0 || name[0] == '_') continue;
+
+ // Cross key settings are with [key]property
+ if (name[0] == '[') {
+ if (!isTrusted) continue;
+ deferred.push_back(&prop);
+ } else {
+ keyHistory->putProp(name, prop, time);
+ }
+ }
+ }
+
+ // handle remote properties, if any
+ for (const auto propptr : deferred) {
+ const auto &prop = *propptr;
+ const std::string &name = prop.getName();
+ size_t end = name.find_first_of(']'); // TODO: handle nested [] or escape?
+ if (end == 0) continue;
+ std::string remoteKey = name.substr(1, end - 1);
+ std::string remoteName = name.substr(end + 1);
+ if (remoteKey.size() == 0 || remoteName.size() == 0) continue;
+ std::shared_ptr<KeyHistory> remoteKeyHistory;
+ {
+ std::lock_guard lock(mLock);
+ auto it = mHistory.find(remoteKey);
+ if (it == mHistory.end()) continue;
+ remoteKeyHistory = it->second;
+ }
+ std::lock_guard(getLockForKey(remoteKey));
+ remoteKeyHistory->putProp(remoteName, prop, time);
+ }
+ return NO_ERROR;
+ }
+
+ template <typename T>
+ status_t get(const std::string &key, const std::string &property,
+ T* value, int32_t uidCheck = -1, int64_t time = 0) const {
+ std::shared_ptr<KeyHistory> keyHistory;
+ {
+ std::lock_guard lock(mLock);
+ const auto it = mHistory.find(key);
+ if (it == mHistory.end()) return BAD_VALUE;
+ keyHistory = it->second;
+ }
+ std::lock_guard lock(getLockForKey(key));
+ return keyHistory->checkPermission(uidCheck)
+ ?: keyHistory->getValue(property, value, time);
+ }
+
+ /**
+ * Individual property put.
+ *
+ * Put takes in a time (if none is provided then BOOTTIME is used).
+ */
+ template <typename T>
+ status_t put(const std::string &url, T &&e, int64_t time = 0) {
+ std::string key;
+ std::string prop;
+ std::shared_ptr<KeyHistory> keyHistory =
+ getKeyHistoryFromUrl(url, &key, &prop);
+ if (keyHistory == nullptr) return BAD_VALUE;
+ if (time == 0) time = systemTime(SYSTEM_TIME_BOOTTIME);
+ std::lock_guard lock(getLockForKey(key));
+ keyHistory->putValue(prop, std::forward<T>(e), time);
+ return NO_ERROR;
+ }
+
+ /**
+ * Individual property get
+ */
+ template <typename T>
+ status_t get(const std::string &url, T* value, int32_t uidCheck, int64_t time = 0) const {
+ std::string key;
+ std::string prop;
+ std::shared_ptr<KeyHistory> keyHistory =
+ getKeyHistoryFromUrl(url, &key, &prop);
+ if (keyHistory == nullptr) return BAD_VALUE;
+
+ std::lock_guard lock(getLockForKey(key));
+ return keyHistory->checkPermission(uidCheck)
+ ?: keyHistory->getValue(prop, value, time);
+ }
+
+ /**
+ * Individual property get with default
+ */
+ template <typename T>
+ T get(const std::string &url, const T &defaultValue, int32_t uidCheck,
+ int64_t time = 0) const {
+ T value;
+ return get(url, &value, uidCheck, time) == NO_ERROR
+ ? value : defaultValue;
+ }
+
+ /**
+ * Returns number of keys in the Time Machine.
+ */
+ size_t size() const {
+ std::lock_guard lock(mLock);
+ return mHistory.size();
+ }
+
+ /**
+ * Clears all properties from the Time Machine.
+ */
+ void clear() {
+ std::lock_guard lock(mLock);
+ mHistory.clear();
+ }
+
+ /**
+ * Returns a pair consisting of the TimeMachine state as a string
+ * and the number of lines in the string.
+ *
+ * The number of lines in the returned pair is used as an optimization
+ * for subsequent line limiting.
+ *
+ * \param lines the maximum number of lines in the string returned.
+ * \param key selects only that key.
+ * \param time to start the dump from.
+ */
+ std::pair<std::string, int32_t> dump(
+ int32_t lines = INT32_MAX, const std::string &key = {}, int64_t time = 0) const {
+ std::lock_guard lock(mLock);
+ if (!key.empty()) { // use std::regex
+ const auto it = mHistory.find(key);
+ if (it == mHistory.end()) return {};
+ std::lock_guard lock(getLockForKey(it->first));
+ return it->second->dump(lines, time);
+ }
+
+ std::stringstream ss;
+ int32_t ll = lines;
+ for (const auto &[lkey, lhist] : mHistory) {
+ std::lock_guard lock(getLockForKey(lkey));
+ if (lines <= 0) break;
+ auto [s, l] = lhist->dump(ll, time);
+ ss << s;
+ ll -= l;
+ }
+ return { ss.str(), lines - ll };
+ }
+
+private:
+
+ // Obtains the lock for a KeyHistory.
+ std::mutex &getLockForKey(const std::string &key) const {
+ return mKeyLocks[std::hash<std::string>{}(key) % std::size(mKeyLocks)];
+ }
+
+ // Finds a KeyHistory from a URL. Returns nullptr if not found.
+ std::shared_ptr<KeyHistory> getKeyHistoryFromUrl(
+ std::string url, std::string* key, std::string *prop) const {
+ std::lock_guard lock(mLock);
+
+ auto it = mHistory.upper_bound(url);
+ if (it == mHistory.begin()) {
+ return nullptr;
+ }
+ --it; // go to the actual key, if it exists.
+
+ const std::string& itKey = it->first;
+ if (strncmp(itKey.c_str(), url.c_str(), itKey.size())) {
+ return nullptr;
+ }
+ if (key) *key = itKey;
+ if (prop) *prop = url.substr(itKey.size() + 1);
+ return it->second;
+ }
+
+ // GUARDED_BY mLock
+ /**
+ * Garbage collects if the TimeMachine size exceeds the high water mark.
+ *
+ * \param garbage a type-erased vector of elements to be destroyed
+ * outside of lock. Move large items to be destroyed here.
+ *
+ * \return true if garbage collection was done.
+ */
+ bool gc_l(std::vector<std::any>& garbage) {
+ // TODO: something better than this for garbage collection.
+ if (mHistory.size() < mKeyHighWaterMark) return false;
+
+ ALOGD("%s: garbage collection", __func__);
+
+ // erase everything explicitly expired.
+ std::multimap<int64_t, std::string> accessList;
+ // use a stale vector with precise type to avoid type erasure overhead in garbage
+ std::vector<std::shared_ptr<KeyHistory>> stale;
+
+ for (auto it = mHistory.begin(); it != mHistory.end();) {
+ const std::string& key = it->first;
+ std::shared_ptr<KeyHistory> &keyHist = it->second;
+
+ std::lock_guard lock(getLockForKey(it->first));
+ int64_t expireTime = keyHist->getValue("_expire", -1 /* default */);
+ if (expireTime != -1) {
+ stale.emplace_back(std::move(it->second));
+ it = mHistory.erase(it);
+ } else {
+ accessList.emplace(keyHist->getLastModificationTime(), key);
+ ++it;
+ }
+ }
+
+ if (mHistory.size() > mKeyLowWaterMark) {
+ const size_t toDelete = mHistory.size() - mKeyLowWaterMark;
+ auto it = accessList.begin();
+ for (size_t i = 0; i < toDelete; ++i) {
+ auto it2 = mHistory.find(it->second);
+ stale.emplace_back(std::move(it2->second));
+ mHistory.erase(it2);
+ ++it;
+ }
+ }
+ garbage.emplace_back(std::move(accessList));
+ garbage.emplace_back(std::move(stale));
+
+ ALOGD("%s(%zu, %zu): key size:%zu",
+ __func__, mKeyLowWaterMark, mKeyHighWaterMark,
+ mHistory.size());
+ return true;
+ }
+
+ const size_t mKeyLowWaterMark = kKeyLowWaterMark;
+ const size_t mKeyHighWaterMark = kKeyHighWaterMark;
+
+ /**
+ * Locking Strategy
+ *
+ * Each key in the History has a KeyHistory. To get a shared pointer to
+ * the KeyHistory requires a lookup of mHistory under mLock. Once the shared
+ * pointer to KeyHistory is obtained, the mLock for mHistory can be released.
+ *
+ * Once the shared pointer to the key's KeyHistory is obtained, the KeyHistory
+ * can be locked for read and modification through the method getLockForKey().
+ *
+ * Instead of having a mutex per KeyHistory, we use a hash striped lock
+ * which assigns a mutex based on the hash of the key string.
+ *
+ * Once the last shared pointer reference to KeyHistory is released, it is
+ * destroyed. This is done through the garbage collection method.
+ *
+ * This two level locking allows multiple threads to access the TimeMachine
+ * in parallel.
+ */
+
+ mutable std::mutex mLock; // Lock for mHistory
+ History mHistory; // GUARDED_BY mLock
+
+ // KEY_LOCKS is the number of mutexes for keys.
+ // It need not be a power of 2, but faster that way.
+ static inline constexpr size_t KEY_LOCKS = 256;
+ mutable std::mutex mKeyLocks[KEY_LOCKS]; // Hash-striped lock for KeyHistory based on key.
+};
+
+} // namespace android::mediametrics
diff --git a/services/mediametrics/TransactionLog.h b/services/mediametrics/TransactionLog.h
new file mode 100644
index 0000000..0c5d12b
--- /dev/null
+++ b/services/mediametrics/TransactionLog.h
@@ -0,0 +1,279 @@
+/*
+ * 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 <any>
+#include <map>
+#include <sstream>
+#include <string>
+
+#include <media/MediaMetricsItem.h>
+
+namespace android::mediametrics {
+
+/**
+ * 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
+ * just make this submit order).
+ *
+ * These Views have a cost in shared pointer storage, so they aren't quite free.
+ *
+ * The TransactionLog is NOT thread safe.
+ */
+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.
+
+ // low water mark
+ static inline constexpr size_t kLogItemsLowWater = 5000;
+ // high water mark
+ static inline constexpr size_t kLogItemsHighWater = 10000;
+
+ // Estimated max data usage is 1KB * kLogItemsHighWater.
+
+ TransactionLog() = default;
+
+ TransactionLog(size_t lowWaterMark, size_t highWaterMark)
+ : mLowWaterMark(lowWaterMark)
+ , mHighWaterMark(highWaterMark) {
+ LOG_ALWAYS_FATAL_IF(highWaterMark <= lowWaterMark,
+ "%s: required that highWaterMark:%zu > lowWaterMark:%zu",
+ __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 mediametrics::Item>& item) {
+ const std::string& key = item->getKey();
+ const int64_t time = item->getTimestamp();
+
+ std::vector<std::any> garbage; // objects destroyed after lock.
+ std::lock_guard lock(mLock);
+
+ (void)gc_l(garbage);
+ mLog.emplace(time, item);
+ mItemMap[key].emplace(time, item);
+ return NO_ERROR; // no errors for now.
+ }
+
+ /**
+ * Returns all records within [startTime, endTime]
+ */
+ 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);
+ }
+
+ /**
+ * Returns all records for a key within [startTime, endTime]
+ */
+ 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);
+ auto mapIt = mItemMap.find(key);
+ if (mapIt == mItemMap.end()) return {};
+ return getItemsInRange_l(mapIt->second, startTime, endTime);
+ }
+
+ /**
+ * Returns a pair consisting of the Transaction Log as a string
+ * and the number of lines in the string.
+ *
+ * The number of lines in the returned pair is used as an optimization
+ * for subsequent line limiting.
+ *
+ * \param lines the maximum number of lines in the string returned.
+ */
+ std::pair<std::string, int32_t> dump(int32_t lines) const {
+ std::stringstream ss;
+ int32_t ll = lines;
+ std::lock_guard lock(mLock);
+
+ // All audio items in time order.
+ if (ll > 0) {
+ ss << "Consolidated:\n";
+ --ll;
+ }
+ for (const auto &log : mLog) {
+ if (ll <= 0) break;
+ ss << " " << log.second->toString() << "\n";
+ --ll;
+ }
+
+ // Grouped by item key (category)
+ if (ll > 0) {
+ ss << "Categorized:\n";
+ --ll;
+ }
+ for (const auto &itemMap : mItemMap) {
+ if (ll <= 0) break;
+ ss << " " << itemMap.first << "\n";
+ --ll;
+ for (const auto &item : itemMap.second) {
+ if (ll <= 0) break;
+ ss << " { " << item.first << ", " << item.second->toString() << " }\n";
+ --ll;
+ }
+ }
+ return { ss.str(), lines - ll };
+ }
+
+ /**
+ * Returns number of Items in the TransactionLog.
+ */
+ size_t size() const {
+ std::lock_guard lock(mLock);
+ return mLog.size();
+ }
+
+ /**
+ * Clears all Items from the TransactionLog.
+ */
+ // TODO: Garbage Collector, sweep and expire old values
+ void clear() {
+ std::lock_guard lock(mLock);
+ mLog.clear();
+ mItemMap.clear();
+ }
+
+private:
+ using MapTimeItem =
+ std::multimap<int64_t /* time */, std::shared_ptr<const mediametrics::Item>>;
+
+ // GUARDED_BY mLock
+ /**
+ * Garbage collects if the TimeMachine size exceeds the high water mark.
+ *
+ * \param garbage a type-erased vector of elements to be destroyed
+ * outside of lock. Move large items to be destroyed here.
+ *
+ * \return true if garbage collection was done.
+ */
+ bool gc_l(std::vector<std::any>& garbage) {
+ if (mLog.size() < mHighWaterMark) return false;
+
+ ALOGD("%s: garbage collection", __func__);
+
+ auto eraseEnd = mLog.begin();
+ size_t toRemove = mLog.size() - mLowWaterMark;
+ // 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 mediametrics::Item>> stale;
+
+ for (size_t i = 0; i < toRemove; ++i) {
+ stale.emplace_back(std::move(eraseEnd->second));
+ ++eraseEnd; // amortized O(1)
+ }
+ // ensure that eraseEnd is an lower bound on timeToErase.
+ const int64_t timeToErase = eraseEnd->first;
+ while (eraseEnd != mLog.end()) {
+ auto it = eraseEnd;
+ --it; // amortized O(1)
+ if (it->first != timeToErase) {
+ break; // eraseEnd represents a unique time jump.
+ }
+ stale.emplace_back(std::move(eraseEnd->second));
+ ++eraseEnd;
+ }
+
+ mLog.erase(mLog.begin(), eraseEnd); // O(ptr_diff)
+
+ size_t itemMapCount = 0;
+ for (auto it = mItemMap.begin(); it != mItemMap.end();) {
+ auto &keyHist = it->second;
+ auto it2 = keyHist.lower_bound(timeToErase);
+ if (it2 == keyHist.end()) {
+ garbage.emplace_back(std::move(keyHist)); // directly move keyhist to garbage
+ it = mItemMap.erase(it);
+ } else {
+ for (auto it3 = keyHist.begin(); it3 != it2; ++it3) {
+ stale.emplace_back(std::move(it3->second));
+ }
+ keyHist.erase(keyHist.begin(), it2);
+ itemMapCount += keyHist.size();
+ ++it;
+ }
+ }
+
+ garbage.emplace_back(std::move(stale));
+
+ ALOGD("%s(%zu, %zu): log size:%zu item map size:%zu, item map items:%zu",
+ __func__, mLowWaterMark, mHighWaterMark,
+ mLog.size(), mItemMap.size(), itemMapCount);
+ return true;
+ }
+
+ 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);
+ if (it == map.end()) return {};
+
+ auto it2 = map.upper_bound(endTime);
+
+ std::vector<std::shared_ptr<const mediametrics::Item>> ret;
+ while (it != it2) {
+ ret.push_back(it->second);
+ ++it;
+ }
+ return ret;
+ }
+
+ const size_t mLowWaterMark = kLogItemsHighWater;
+ const size_t mHighWaterMark = kLogItemsHighWater;
+
+ mutable std::mutex mLock;
+
+ // GUARDED_BY mLock
+ MapTimeItem mLog;
+
+ // GUARDED_BY mLock
+ std::map<std::string /* item_key */, MapTimeItem> mItemMap;
+};
+
+} // namespace android::mediametrics
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/mediaanalytics/iface_statsd.cpp b/services/mediametrics/iface_statsd.cpp
similarity index 78%
rename from services/mediaanalytics/iface_statsd.cpp
rename to services/mediametrics/iface_statsd.cpp
index 6845f06..3a1eea7 100644
--- a/services/mediaanalytics/iface_statsd.cpp
+++ b/services/mediametrics/iface_statsd.cpp
@@ -27,20 +27,21 @@
#include <pthread.h>
#include <unistd.h>
+#include <memory>
#include <string.h>
#include <pwd.h>
-#include "MediaAnalyticsService.h"
+#include "MediaMetricsService.h"
#include "iface_statsd.h"
#include <statslog.h>
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.
//
@@ -48,11 +49,11 @@
struct statsd_hooks {
const char *key;
- bool (*handler)(MediaAnalyticsItem *);
+ bool (*handler)(const mediametrics::Item *);
};
// keep this sorted, so we can do binary searches
-struct statsd_hooks statsd_handlers[] =
+static constexpr struct statsd_hooks statsd_handlers[] =
{
{ "audiopolicy", statsd_audiopolicy },
{ "audiorecord", statsd_audiorecord },
@@ -60,6 +61,7 @@
{ "audiotrack", statsd_audiotrack },
{ "codec", statsd_codec},
{ "drm.vendor.Google.WidevineCDM", statsd_widevineCDM },
+ { "drmmanager", statsd_drmmanager },
{ "extractor", statsd_extractor },
{ "mediadrm", statsd_mediadrm },
{ "nuplayer", statsd_nuplayer },
@@ -67,9 +69,8 @@
{ "recorder", statsd_recorder },
};
-
// give me a record, i'll look at the type and upload appropriately
-bool dump2Statsd(MediaAnalyticsItem *item) {
+bool dump2Statsd(const std::shared_ptr<const mediametrics::Item>& item) {
if (item == NULL) return false;
// get the key
@@ -80,10 +81,9 @@
return false;
}
- int i;
- for(i = 0;i < sizeof(statsd_handlers) / sizeof(statsd_handlers[0]) ; i++) {
- if (key == statsd_handlers[i].key) {
- return (*statsd_handlers[i].handler)(item);
+ for (const auto &statsd_handler : statsd_handlers) {
+ if (key == statsd_handler.key) {
+ return statsd_handler.handler(item.get());
}
}
return false;
diff --git a/services/mediametrics/iface_statsd.h b/services/mediametrics/iface_statsd.h
new file mode 100644
index 0000000..19505a4
--- /dev/null
+++ b/services/mediametrics/iface_statsd.h
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+namespace android {
+
+extern bool enabled_statsd;
+
+// component specific dumpers
+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 mediametrics::Item *);
+extern bool statsd_widevineCDM(const mediametrics::Item *);
+extern bool statsd_drmmanager(const mediametrics::Item *);
+
+} // namespace android
diff --git a/services/mediaanalytics/main_mediametrics.cpp b/services/mediametrics/main_mediametrics.cpp
similarity index 72%
rename from services/mediaanalytics/main_mediametrics.cpp
rename to services/mediametrics/main_mediametrics.cpp
index 8020a03..ec392e2 100644
--- a/services/mediaanalytics/main_mediametrics.cpp
+++ b/services/mediametrics/main_mediametrics.cpp
@@ -16,33 +16,33 @@
#define LOG_TAG "mediametrics"
//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include "MediaMetricsService.h"
#include <binder/IPCThreadState.h>
-#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
-#include <utils/Log.h>
-//#include "RegisterExtensions.h"
+#include <binder/ProcessState.h>
-// from LOCAL_C_INCLUDES
-#include "MediaAnalyticsService.h"
-
-using namespace android;
int main(int argc __unused, char **argv __unused)
{
+ using namespace android;
+
signal(SIGPIPE, SIG_IGN);
// 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], "media.metrics");
+ strcpy(argv[0], MediaMetricsService::kServiceName);
- sp<ProcessState> proc(ProcessState::self());
- sp<IServiceManager> sm(defaultServiceManager());
- ALOGI("ServiceManager: %p", sm.get());
+ defaultServiceManager()->addService(
+ String16(MediaMetricsService::kServiceName), new MediaMetricsService());
- MediaAnalyticsService::instantiate();
-
- ProcessState::self()->startThreadPool();
+ sp<ProcessState> processState(ProcessState::self());
+ // processState->setThreadPoolMaxThreadCount(8);
+ processState->startThreadPool();
IPCThreadState::self()->joinThreadPool();
+
+ return EXIT_SUCCESS;
}
diff --git a/services/mediaanalytics/mediametrics.rc b/services/mediametrics/mediametrics.rc
similarity index 100%
rename from services/mediaanalytics/mediametrics.rc
rename to services/mediametrics/mediametrics.rc
diff --git a/services/mediaanalytics/statsd_audiopolicy.cpp b/services/mediametrics/statsd_audiopolicy.cpp
similarity index 95%
rename from services/mediaanalytics/statsd_audiopolicy.cpp
rename to services/mediametrics/statsd_audiopolicy.cpp
index 95cb274..634c801 100644
--- a/services/mediaanalytics/statsd_audiopolicy.cpp
+++ b/services/mediametrics/statsd_audiopolicy.cpp
@@ -31,18 +31,18 @@
#include <statslog.h>
-#include "MediaAnalyticsService.h"
+#include "MediaMetricsService.h"
#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h"
#include "iface_statsd.h"
namespace android {
-bool statsd_audiopolicy(MediaAnalyticsItem *item)
+bool statsd_audiopolicy(const mediametrics::Item *item)
{
if (item == NULL) return false;
// these go into the statsd wrapper
- nsecs_t timestamp = 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/mediaanalytics/statsd_audiorecord.cpp b/services/mediametrics/statsd_audiorecord.cpp
similarity index 96%
rename from services/mediaanalytics/statsd_audiorecord.cpp
rename to services/mediametrics/statsd_audiorecord.cpp
index 7c7a62c..69d1661 100644
--- a/services/mediaanalytics/statsd_audiorecord.cpp
+++ b/services/mediametrics/statsd_audiorecord.cpp
@@ -31,18 +31,18 @@
#include <statslog.h>
-#include "MediaAnalyticsService.h"
+#include "MediaMetricsService.h"
#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h"
#include "iface_statsd.h"
namespace android {
-bool statsd_audiorecord(MediaAnalyticsItem *item)
+bool statsd_audiorecord(const mediametrics::Item *item)
{
if (item == NULL) return false;
// these go into the statsd wrapper
- nsecs_t timestamp = 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/mediaanalytics/statsd_audiothread.cpp b/services/mediametrics/statsd_audiothread.cpp
similarity index 97%
rename from services/mediaanalytics/statsd_audiothread.cpp
rename to services/mediametrics/statsd_audiothread.cpp
index e9d6b17..300151b 100644
--- a/services/mediaanalytics/statsd_audiothread.cpp
+++ b/services/mediametrics/statsd_audiothread.cpp
@@ -31,18 +31,18 @@
#include <statslog.h>
-#include "MediaAnalyticsService.h"
+#include "MediaMetricsService.h"
#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h"
#include "iface_statsd.h"
namespace android {
-bool statsd_audiothread(MediaAnalyticsItem *item)
+bool statsd_audiothread(const mediametrics::Item *item)
{
if (item == NULL) return false;
// these go into the statsd wrapper
- nsecs_t timestamp = 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/mediaanalytics/statsd_audiotrack.cpp b/services/mediametrics/statsd_audiotrack.cpp
similarity index 96%
rename from services/mediaanalytics/statsd_audiotrack.cpp
rename to services/mediametrics/statsd_audiotrack.cpp
index 57cda99..397cdf3 100644
--- a/services/mediaanalytics/statsd_audiotrack.cpp
+++ b/services/mediametrics/statsd_audiotrack.cpp
@@ -31,18 +31,18 @@
#include <statslog.h>
-#include "MediaAnalyticsService.h"
+#include "MediaMetricsService.h"
#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h"
#include "iface_statsd.h"
namespace android {
-bool statsd_audiotrack(MediaAnalyticsItem *item)
+bool statsd_audiotrack(const mediametrics::Item *item)
{
if (item == NULL) return false;
// these go into the statsd wrapper
- nsecs_t timestamp = 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/mediaanalytics/statsd_codec.cpp b/services/mediametrics/statsd_codec.cpp
similarity index 97%
rename from services/mediaanalytics/statsd_codec.cpp
rename to services/mediametrics/statsd_codec.cpp
index bf82e50..214c51a 100644
--- a/services/mediaanalytics/statsd_codec.cpp
+++ b/services/mediametrics/statsd_codec.cpp
@@ -31,18 +31,18 @@
#include <statslog.h>
-#include "MediaAnalyticsService.h"
+#include "MediaMetricsService.h"
#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h"
#include "iface_statsd.h"
namespace android {
-bool statsd_codec(MediaAnalyticsItem *item)
+bool statsd_codec(const mediametrics::Item *item)
{
if (item == NULL) return false;
// these go into the statsd wrapper
- nsecs_t timestamp = 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/mediaanalytics/statsd_drm.cpp b/services/mediametrics/statsd_drm.cpp
similarity index 69%
rename from services/mediaanalytics/statsd_drm.cpp
rename to services/mediametrics/statsd_drm.cpp
index 902483a..b12f4f3 100644
--- a/services/mediaanalytics/statsd_drm.cpp
+++ b/services/mediametrics/statsd_drm.cpp
@@ -30,7 +30,7 @@
#include <string.h>
#include <pwd.h>
-#include "MediaAnalyticsService.h"
+#include "MediaMetricsService.h"
#include "iface_statsd.h"
#include <statslog.h>
@@ -38,11 +38,11 @@
namespace android {
// mediadrm
-bool statsd_mediadrm(MediaAnalyticsItem *item)
+bool statsd_mediadrm(const mediametrics::Item *item)
{
if (item == NULL) return false;
- nsecs_t timestamp = 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(MediaAnalyticsItem *item)
+bool statsd_widevineCDM(const mediametrics::Item *item)
{
if (item == NULL) return false;
- nsecs_t timestamp = item->getTimestamp();
+ const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
std::string pkgName = item->getPkgName();
int64_t pkgVersionCode = item->getPkgVersionCode();
int64_t mediaApexVersion = 0;
@@ -104,4 +104,38 @@
return true;
}
+// drmmanager
+bool statsd_drmmanager(const mediametrics::Item *item)
+{
+ if (item == NULL) return false;
+
+ const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
+ std::string pkgName = item->getPkgName();
+ int64_t pkgVersionCode = item->getPkgVersionCode();
+ int64_t mediaApexVersion = 0;
+
+ char *plugin_id = NULL;
+ (void) item->getCString("plugin_id", &plugin_id);
+ char *description = NULL;
+ (void) item->getCString("description", &description);
+ int32_t method_id = -1;
+ (void) item->getInt32("method_id", &method_id);
+ char *mime_types = NULL;
+ (void) item->getCString("mime_types", &mime_types);
+
+ if (enabled_statsd) {
+ android::util::stats_write(android::util::MEDIAMETRICS_DRMMANAGER_REPORTED,
+ timestamp, pkgName.c_str(), pkgVersionCode,
+ mediaApexVersion,
+ plugin_id, description,
+ method_id, mime_types);
+ } else {
+ ALOGV("NOT sending: drmmanager data");
+ }
+
+ free(plugin_id);
+ free(description);
+ free(mime_types);
+ return true;
+}
} // namespace android
diff --git a/services/mediaanalytics/statsd_extractor.cpp b/services/mediametrics/statsd_extractor.cpp
similarity index 93%
rename from services/mediaanalytics/statsd_extractor.cpp
rename to services/mediametrics/statsd_extractor.cpp
index d84930c..8574358 100644
--- a/services/mediaanalytics/statsd_extractor.cpp
+++ b/services/mediametrics/statsd_extractor.cpp
@@ -31,18 +31,18 @@
#include <statslog.h>
-#include "MediaAnalyticsService.h"
+#include "MediaMetricsService.h"
#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h"
#include "iface_statsd.h"
namespace android {
-bool statsd_extractor(MediaAnalyticsItem *item)
+bool statsd_extractor(const mediametrics::Item *item)
{
if (item == NULL) return false;
// these go into the statsd wrapper
- nsecs_t timestamp = 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/mediaanalytics/statsd_nuplayer.cpp b/services/mediametrics/statsd_nuplayer.cpp
similarity index 96%
rename from services/mediaanalytics/statsd_nuplayer.cpp
rename to services/mediametrics/statsd_nuplayer.cpp
index e6e0f2c..df7e59f 100644
--- a/services/mediaanalytics/statsd_nuplayer.cpp
+++ b/services/mediametrics/statsd_nuplayer.cpp
@@ -31,7 +31,7 @@
#include <statslog.h>
-#include "MediaAnalyticsService.h"
+#include "MediaMetricsService.h"
#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h"
#include "iface_statsd.h"
@@ -41,12 +41,12 @@
* handles nuplayer AND nuplayer2
* checks for the union of what the two players generate
*/
-bool statsd_nuplayer(MediaAnalyticsItem *item)
+bool statsd_nuplayer(const mediametrics::Item *item)
{
if (item == NULL) return false;
// these go into the statsd wrapper
- nsecs_t timestamp = 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/mediaanalytics/statsd_recorder.cpp b/services/mediametrics/statsd_recorder.cpp
similarity index 97%
rename from services/mediaanalytics/statsd_recorder.cpp
rename to services/mediametrics/statsd_recorder.cpp
index d286f00..4de1746 100644
--- a/services/mediaanalytics/statsd_recorder.cpp
+++ b/services/mediametrics/statsd_recorder.cpp
@@ -31,18 +31,18 @@
#include <statslog.h>
-#include "MediaAnalyticsService.h"
+#include "MediaMetricsService.h"
#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h"
#include "iface_statsd.h"
namespace android {
-bool statsd_recorder(MediaAnalyticsItem *item)
+bool statsd_recorder(const mediametrics::Item *item)
{
if (item == NULL) return false;
// these go into the statsd wrapper
- nsecs_t timestamp = 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
new file mode 100644
index 0000000..bdeda30
--- /dev/null
+++ b/services/mediametrics/tests/Android.bp
@@ -0,0 +1,27 @@
+cc_test {
+ name: "mediametrics_tests",
+ test_suites: ["device-tests"],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+
+ include_dirs: [
+ "frameworks/av/services/mediametrics",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "liblog",
+ "libmediametrics",
+ "libmediametricsservice",
+ "libmediautils",
+ "libutils",
+ ],
+
+ srcs: [
+ "mediametrics_tests.cpp",
+ ],
+}
diff --git a/services/mediametrics/tests/build_and_run_all_unit_tests.sh b/services/mediametrics/tests/build_and_run_all_unit_tests.sh
new file mode 100755
index 0000000..382be47
--- /dev/null
+++ b/services/mediametrics/tests/build_and_run_all_unit_tests.sh
@@ -0,0 +1,24 @@
+#!/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
+
+echo "========================================"
+
+echo "testing mediametrics"
+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
new file mode 100644
index 0000000..27b72eb
--- /dev/null
+++ b/services/mediametrics/tests/mediametrics_tests.cpp
@@ -0,0 +1,848 @@
+/*
+ * 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 "mediametrics_tests"
+#include <utils/Log.h>
+
+#include "MediaMetricsService.h"
+
+#include <stdio.h>
+
+#include <gtest/gtest.h>
+#include <media/MediaMetricsItem.h>
+
+using namespace android;
+
+static size_t countNewlines(const char *s) {
+ size_t count = 0;
+ while ((s = strchr(s, '\n')) != nullptr) {
+ ++s;
+ ++count;
+ }
+ 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 MediaMetricsService();
+ status_t status;
+
+ // random keys ignored when empty
+ std::unique_ptr<mediametrics::Item> random_key(mediametrics::Item::create("random_key"));
+ status = mediaMetrics->submit(random_key.get());
+ ASSERT_EQ(PERMISSION_DENIED, status);
+
+ // random keys ignored with data
+ random_key->setInt32("foo", 10);
+ status = mediaMetrics->submit(random_key.get());
+ ASSERT_EQ(PERMISSION_DENIED, status);
+
+ // known keys ignored if empty
+ std::unique_ptr<mediametrics::Item> audiotrack_key(mediametrics::Item::create("audiotrack"));
+ status = mediaMetrics->submit(audiotrack_key.get());
+ ASSERT_EQ(BAD_VALUE, status);
+
+ // known keys not ignored if not empty
+ audiotrack_key->addInt32("foo", 10);
+ status = mediaMetrics->submit(audiotrack_key.get());
+ ASSERT_EQ(NO_ERROR, status);
+
+
+ /*
+ // fluent style that goes directly to mediametrics
+ ASSERT_EQ(true, mediametrics::Item("audiorecord")
+ .setInt32("value", 2)
+ .addInt32("bar", 1)
+ .addInt32("value", 3)
+ .selfrecord());
+ */
+
+ 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) {
+ mediametrics::Item item("audiorecord");
+
+ item.setInt32("value", 2).addInt32("bar", 3).addInt32("value", 4);
+
+ int32_t i32;
+ ASSERT_TRUE(item.getInt32("value", &i32));
+ ASSERT_EQ(6, i32);
+
+ ASSERT_TRUE(item.getInt32("bar", &i32));
+ ASSERT_EQ(3, i32);
+
+ item.setInt64("big", INT64_MAX).setInt64("smaller", INT64_MAX - 1).addInt64("smaller", -2);
+
+ int64_t i64;
+ ASSERT_TRUE(item.getInt64("big", &i64));
+ ASSERT_EQ(INT64_MAX, i64);
+
+ ASSERT_TRUE(item.getInt64("smaller", &i64));
+ ASSERT_EQ(INT64_MAX - 3, i64);
+
+ item.setDouble("precise", 10.5).setDouble("small", 0.125).addDouble("precise", 0.25);
+
+ double d;
+ ASSERT_TRUE(item.getDouble("precise", &d));
+ ASSERT_EQ(10.75, d);
+
+ ASSERT_TRUE(item.getDouble("small", &d));
+ ASSERT_EQ(0.125, d);
+
+ char *s;
+ item.setCString("name", "Frank").setCString("mother", "June").setCString("mother", "July");
+ ASSERT_TRUE(item.getCString("name", &s));
+ ASSERT_EQ(0, strcmp(s, "Frank"));
+ free(s);
+
+ ASSERT_TRUE(item.getCString("mother", &s));
+ ASSERT_EQ(0, strcmp(s, "July")); // "July" overwrites "June"
+ free(s);
+
+ item.setRate("burgersPerHour", 5, 2);
+ int64_t b, h;
+ ASSERT_TRUE(item.getRate("burgersPerHour", &b, &h, &d));
+ ASSERT_EQ(5, b);
+ ASSERT_EQ(2, h);
+ ASSERT_EQ(2.5, d);
+
+ item.addRate("burgersPerHour", 4, 2);
+ ASSERT_TRUE(item.getRate("burgersPerHour", &b, &h, &d));
+ ASSERT_EQ(9, b);
+ ASSERT_EQ(4, h);
+ ASSERT_EQ(2.25, d);
+
+ printf("item: %s\n", item.toString().c_str());
+ fflush(stdout);
+
+ 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) {
+ mediametrics::Item item("TheBigOne");
+ constexpr size_t count = 10000;
+
+ for (size_t i = 0; i < count; ++i) {
+ item.setInt32(std::to_string(i).c_str(), i);
+ }
+ for (size_t i = 0; i < count; ++i) {
+ int32_t i32;
+ ASSERT_TRUE(item.getInt32(std::to_string(i).c_str(), &i32));
+ ASSERT_EQ((int32_t)i, i32);
+ }
+}
+
+TEST(mediametrics_tests, superbig_item_removal) {
+ mediametrics::Item item("TheOddBigOne");
+ constexpr size_t count = 10000;
+
+ for (size_t i = 0; i < count; ++i) {
+ item.setInt32(std::to_string(i).c_str(), i);
+ }
+ for (size_t i = 0; i < count; i += 2) {
+ item.filter(std::to_string(i).c_str()); // filter out all the evens.
+ }
+ for (size_t i = 0; i < count; ++i) {
+ int32_t i32;
+ if (i & 1) { // check to see that only the odds are left.
+ ASSERT_TRUE(item.getInt32(std::to_string(i).c_str(), &i32));
+ ASSERT_EQ((int32_t)i, i32);
+ } else {
+ ASSERT_FALSE(item.getInt32(std::to_string(i).c_str(), &i32));
+ }
+ }
+}
+
+TEST(mediametrics_tests, superbig_item_removal2) {
+ mediametrics::Item item("TheOne");
+ constexpr size_t count = 10000;
+
+ for (size_t i = 0; i < count; ++i) {
+ item.setInt32(std::to_string(i).c_str(), i);
+ }
+ static const char *attrs[] = { "1", };
+ item.filterNot(1, attrs);
+
+ for (size_t i = 0; i < count; ++i) {
+ int32_t i32;
+ if (i == 1) { // check to see that there is only one
+ ASSERT_TRUE(item.getInt32(std::to_string(i).c_str(), &i32));
+ ASSERT_EQ((int32_t)i, i32);
+ } else {
+ ASSERT_FALSE(item.getInt32(std::to_string(i).c_str(), &i32));
+ }
+ }
+}
+
+TEST(mediametrics_tests, item_transmutation) {
+ mediametrics::Item item("Alchemist's Stone");
+
+ item.setInt64("convert", 123);
+ int64_t i64;
+ ASSERT_TRUE(item.getInt64("convert", &i64));
+ ASSERT_EQ(123, i64);
+
+ item.addInt32("convert", 2); // changes type of 'convert' from i64 to i32 (and re-init).
+ ASSERT_FALSE(item.getInt64("convert", &i64)); // should be false, no value in i64.
+
+ int32_t i32;
+ ASSERT_TRUE(item.getInt32("convert", &i32)); // check it is i32 and 2 (123 is discarded).
+ ASSERT_EQ(2, i32);
+}
+
+TEST(mediametrics_tests, item_binderization) {
+ mediametrics::Item item;
+ item.setInt32("i32", 1)
+ .setInt64("i64", 2)
+ .setDouble("double", 3.1)
+ .setCString("string", "abc")
+ .setRate("rate", 11, 12);
+
+ Parcel p;
+ item.writeToParcel(&p);
+
+ p.setDataPosition(0); // rewind for reading
+ mediametrics::Item item2;
+ item2.readFromParcel(p);
+
+ ASSERT_EQ(item, item2);
+}
+
+TEST(mediametrics_tests, item_byteserialization) {
+ mediametrics::Item item;
+ item.setInt32("i32", 1)
+ .setInt64("i64", 2)
+ .setDouble("double", 3.1)
+ .setCString("string", "abc")
+ .setRate("rate", 11, 12);
+
+ char *data;
+ size_t length;
+ ASSERT_EQ(0, item.writeToByteString(&data, &length));
+ ASSERT_GT(length, (size_t)0);
+
+ mediametrics::Item item2;
+ item2.readFromByteString(data, length);
+
+ printf("item: %s\n", item.toString().c_str());
+ printf("item2: %s\n", item2.toString().c_str());
+ ASSERT_EQ(item, item2);
+
+ free(data);
+}
+
+TEST(mediametrics_tests, item_iteration) {
+ mediametrics::Item item;
+ item.setInt32("i32", 1)
+ .setInt64("i64", 2)
+ .setDouble("double", 3.125)
+ .setCString("string", "abc")
+ .setRate("rate", 11, 12);
+
+ int mask = 0;
+ for (auto &prop : item) {
+ const char *name = prop.getName();
+ if (!strcmp(name, "i32")) {
+ 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")) {
+ std::string s;
+ ASSERT_TRUE(prop.get(&s));
+ 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();
+ }
+ }
+ ASSERT_EQ(31, mask);
+}
+
+TEST(mediametrics_tests, item_expansion) {
+ mediametrics::LogItem<1> item("I");
+ item.set("i32", (int32_t)1)
+ .set("i64", (int64_t)2)
+ .set("double", (double)3.125)
+ .set("string", "abcdefghijklmnopqrstuvwxyz")
+ .set("rate", std::pair<int64_t, int64_t>(11, 12));
+ ASSERT_TRUE(item.updateHeader());
+
+ mediametrics::Item item2;
+ item2.readFromByteString(item.getBuffer(), item.getLength());
+ ASSERT_EQ((pid_t)-1, item2.getPid());
+ ASSERT_EQ((uid_t)-1, item2.getUid());
+ int mask = 0;
+ for (auto &prop : item2) {
+ const char *name = prop.getName();
+ if (!strcmp(name, "i32")) {
+ int32_t i32;
+ ASSERT_TRUE(prop.get(&i32));
+ ASSERT_EQ(1, i32);
+ mask |= 1;
+ } else if (!strcmp(name, "i64")) {
+ int64_t i64;
+ ASSERT_TRUE(prop.get(&i64));
+ ASSERT_EQ(2, i64);
+ mask |= 2;
+ } else if (!strcmp(name, "double")) {
+ double d;
+ ASSERT_TRUE(prop.get(&d));
+ ASSERT_EQ(3.125, d);
+ mask |= 4;
+ } else if (!strcmp(name, "string")) {
+ std::string s;
+ ASSERT_TRUE(prop.get(&s));
+ ASSERT_EQ("abcdefghijklmnopqrstuvwxyz", s);
+ 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);
+ mask |= 16;
+ } else {
+ FAIL();
+ }
+ }
+ ASSERT_EQ(31, mask);
+}
+
+TEST(mediametrics_tests, item_expansion2) {
+ mediametrics::LogItem<1> item("Bigly");
+ item.setPid(123)
+ .setUid(456);
+ constexpr size_t count = 10000;
+
+ for (size_t i = 0; i < count; ++i) {
+ // printf("recording %zu, %p, len:%zu of %zu remaining:%zu \n", i, item.getBuffer(), item.getLength(), item.getCapacity(), item.getRemaining());
+ item.set(std::to_string(i).c_str(), (int32_t)i);
+ }
+ ASSERT_TRUE(item.updateHeader());
+
+ mediametrics::Item item2;
+ printf("begin buffer:%p length:%zu\n", item.getBuffer(), item.getLength());
+ fflush(stdout);
+ item2.readFromByteString(item.getBuffer(), item.getLength());
+
+ ASSERT_EQ((pid_t)123, item2.getPid());
+ ASSERT_EQ((uid_t)456, item2.getUid());
+ for (size_t i = 0; i < count; ++i) {
+ int32_t i32;
+ ASSERT_TRUE(item2.getInt32(std::to_string(i).c_str(), &i32));
+ ASSERT_EQ((int32_t)i, i32);
+ }
+}
+
+TEST(mediametrics_tests, time_machine_storage) {
+ auto item = std::make_shared<mediametrics::Item>("Key");
+ (*item).set("i32", (int32_t)1)
+ .set("i64", (int64_t)2)
+ .set("double", (double)3.125)
+ .set("string", "abcdefghijklmnopqrstuvwxyz")
+ .set("rate", std::pair<int64_t, int64_t>(11, 12));
+
+ // Let's put the item in
+ android::mediametrics::TimeMachine timeMachine;
+ ASSERT_EQ(NO_ERROR, timeMachine.put(item, true));
+
+ // Can we read the values?
+ int32_t i32;
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key", "i32", &i32, -1));
+ ASSERT_EQ(1, i32);
+
+ int64_t i64;
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key", "i64", &i64, -1));
+ ASSERT_EQ(2, i64);
+
+ double d;
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key", "double", &d, -1));
+ ASSERT_EQ(3.125, d);
+
+ std::string s;
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key", "string", &s, -1));
+ ASSERT_EQ("abcdefghijklmnopqrstuvwxyz", s);
+
+ // Using fully qualified name?
+ i32 = 0;
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key.i32", &i32, -1));
+ ASSERT_EQ(1, i32);
+
+ i64 = 0;
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key.i64", &i64, -1));
+ ASSERT_EQ(2, i64);
+
+ d = 0.;
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key.double", &d, -1));
+ ASSERT_EQ(3.125, d);
+
+ s.clear();
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key.string", &s, -1));
+ ASSERT_EQ("abcdefghijklmnopqrstuvwxyz", s);
+}
+
+TEST(mediametrics_tests, time_machine_remote_key) {
+ 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<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<mediametrics::Item>("Key2");
+ (*item3).set("six", (int32_t)6)
+ .set("[Key1]seven", (int32_t)7); // affects Key1
+
+ ASSERT_EQ(NO_ERROR, timeMachine.put(item3, false)); // remote keys not allowed.
+
+ // Can we read the values?
+ int32_t i32;
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key1.one", &i32, -1));
+ ASSERT_EQ(1, i32);
+
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key1.two", &i32, -1));
+ ASSERT_EQ(2, i32);
+
+ ASSERT_EQ(BAD_VALUE, timeMachine.get("Key1.three", &i32, -1));
+
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key2.three", &i32, -1));
+ ASSERT_EQ(3, i32);
+
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key1.four", &i32, -1));
+ ASSERT_EQ(4, i32);
+
+ ASSERT_EQ(BAD_VALUE, timeMachine.get("Key2.four", &i32, -1));
+
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key1.five", &i32, -1));
+ ASSERT_EQ(5, i32);
+
+ ASSERT_EQ(BAD_VALUE, timeMachine.get("Key2.five", &i32, -1));
+
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key2.six", &i32, -1));
+ ASSERT_EQ(6, i32);
+
+ ASSERT_EQ(BAD_VALUE, timeMachine.get("Key2.seven", &i32, -1));
+}
+
+TEST(mediametrics_tests, time_machine_gc) {
+ auto item = std::make_shared<mediametrics::Item>("Key1");
+ (*item).set("one", (int32_t)1)
+ .set("two", (int32_t)2)
+ .setTimestamp(10);
+
+ android::mediametrics::TimeMachine timeMachine(1, 2); // keep at most 2 keys.
+
+ ASSERT_EQ((size_t)0, timeMachine.size());
+
+ ASSERT_EQ(NO_ERROR, timeMachine.put(item, true));
+
+ ASSERT_EQ((size_t)1, timeMachine.size());
+
+ auto item2 = std::make_shared<mediametrics::Item>("Key2");
+ (*item2).set("three", (int32_t)3)
+ .set("[Key1]three", (int32_t)3)
+ .setTimestamp(11);
+
+ ASSERT_EQ(NO_ERROR, timeMachine.put(item2, true));
+ ASSERT_EQ((size_t)2, timeMachine.size());
+
+ //printf("Before\n%s\n\n", timeMachine.dump().c_str());
+
+ 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
+ .setTimestamp(12);
+
+ ASSERT_EQ(NO_ERROR, timeMachine.put(item3, true));
+
+ ASSERT_EQ((size_t)2, timeMachine.size());
+
+ // Can we read the values?
+ int32_t i32;
+ ASSERT_EQ(BAD_VALUE, timeMachine.get("Key1.one", &i32, -1));
+ ASSERT_EQ(BAD_VALUE, timeMachine.get("Key1.two", &i32, -1));
+ ASSERT_EQ(BAD_VALUE, timeMachine.get("Key1.three", &i32, -1));
+ ASSERT_EQ(BAD_VALUE, timeMachine.get("Key1.four", &i32, -1));
+ ASSERT_EQ(BAD_VALUE, timeMachine.get("Key1.five", &i32, -1));
+
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key2.three", &i32, -1));
+ ASSERT_EQ(3, i32);
+
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key3.six", &i32, -1));
+ ASSERT_EQ(6, i32);
+
+ printf("After\n%s\n", timeMachine.dump().first.c_str());
+}
+
+TEST(mediametrics_tests, transaction_log_gc) {
+ auto item = std::make_shared<mediametrics::Item>("Key1");
+ (*item).set("one", (int32_t)1)
+ .set("two", (int32_t)2)
+ .setTimestamp(10);
+
+ android::mediametrics::TransactionLog transactionLog(1, 2); // keep at most 2 items
+ ASSERT_EQ((size_t)0, transactionLog.size());
+
+ ASSERT_EQ(NO_ERROR, transactionLog.put(item));
+ ASSERT_EQ((size_t)1, transactionLog.size());
+
+ auto item2 = std::make_shared<mediametrics::Item>("Key2");
+ (*item2).set("three", (int32_t)3)
+ .set("[Key1]three", (int32_t)3)
+ .setTimestamp(11);
+
+ ASSERT_EQ(NO_ERROR, transactionLog.put(item2));
+ ASSERT_EQ((size_t)2, transactionLog.size());
+
+ 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
+ .setTimestamp(12);
+
+ ASSERT_EQ(NO_ERROR, transactionLog.put(item3));
+ 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<mediametrics::Item>("audio.1");
+ (*item).set("one", (int32_t)1)
+ .set("two", (int32_t)2)
+ .setTimestamp(10);
+
+ auto item2 = std::make_shared<mediametrics::Item>("audio.1");
+ (*item2).set("three", (int32_t)3)
+ .setTimestamp(11);
+
+ auto item3 = std::make_shared<mediametrics::Item>("audio.2");
+ (*item3).set("four", (int32_t)4)
+ .setTimestamp(12);
+
+ android::mediametrics::AudioAnalytics audioAnalytics;
+
+ // untrusted entities cannot create a new key.
+ ASSERT_EQ(PERMISSION_DENIED, audioAnalytics.submit(item, false /* isTrusted */));
+ ASSERT_EQ(PERMISSION_DENIED, audioAnalytics.submit(item2, false /* isTrusted */));
+
+ // TODO: Verify contents of AudioAnalytics.
+ // Currently there is no getter API in AudioAnalytics besides dump.
+ ASSERT_EQ(9, audioAnalytics.dump(1000).second /* lines */);
+
+ ASSERT_EQ(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(9, audioAnalytics.dump(1000).second /* lines */);
+}
+
+TEST(mediametrics_tests, audio_analytics_dump) {
+ 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<mediametrics::Item>("audio.1");
+ (*item2).set("three", (int32_t)3)
+ .setTimestamp(11);
+
+ auto item3 = std::make_shared<mediametrics::Item>("audio.2");
+ (*item3).set("four", (int32_t)4)
+ .setTimestamp(12);
+
+ android::mediametrics::AudioAnalytics audioAnalytics;
+
+ 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 */));
+ ASSERT_EQ(NO_ERROR, audioAnalytics.submit(item3, true /* isTrusted */));
+
+ // find out how many lines we have.
+ auto [string, lines] = audioAnalytics.dump(1000);
+ ASSERT_EQ(lines, (int32_t) countNewlines(string.c_str()));
+
+ printf("AudioAnalytics: %s", string.c_str());
+ // ensure that dump operates over those lines.
+ for (int32_t ll = 0; ll < lines; ++ll) {
+ auto [s, l] = audioAnalytics.dump(ll);
+ ASSERT_EQ(ll, l);
+ ASSERT_EQ(ll, (int32_t) countNewlines(s.c_str()));
+ }
+}
diff --git a/services/mediaresourcemanager/Android.bp b/services/mediaresourcemanager/Android.bp
index d468406..a3519d5 100644
--- a/services/mediaresourcemanager/Android.bp
+++ b/services/mediaresourcemanager/Android.bp
@@ -12,6 +12,7 @@
"libmedia",
"libmediautils",
"libbinder",
+ "libbinder_ndk",
"libutils",
"liblog",
],
diff --git a/services/mediaresourcemanager/ResourceManagerService.cpp b/services/mediaresourcemanager/ResourceManagerService.cpp
index 45eea0f..877c44d 100644
--- a/services/mediaresourcemanager/ResourceManagerService.cpp
+++ b/services/mediaresourcemanager/ResourceManagerService.cpp
@@ -19,10 +19,13 @@
#define LOG_TAG "ResourceManagerService"
#include <utils/Log.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
#include <binder/IMediaResourceMonitor.h>
#include <binder/IServiceManager.h>
#include <cutils/sched_policy.h>
#include <dirent.h>
+#include <media/MediaResourcePolicy.h>
#include <media/stagefright/ProcessInfo.h>
#include <mediautils/BatteryNotifier.h>
#include <mediautils/SchedulingPolicyService.h>
@@ -37,43 +40,38 @@
namespace android {
-namespace {
+DeathNotifier::DeathNotifier(const std::shared_ptr<ResourceManagerService> &service,
+ int pid, int64_t clientId)
+ : mService(service), mPid(pid), mClientId(clientId) {}
-class DeathNotifier : public IBinder::DeathRecipient {
-public:
- DeathNotifier(const wp<ResourceManagerService> &service, int pid, int64_t clientId)
- : mService(service), mPid(pid), mClientId(clientId) {}
+//static
+void DeathNotifier::BinderDiedCallback(void* cookie) {
+ auto thiz = static_cast<DeathNotifier*>(cookie);
+ thiz->binderDied();
+}
- virtual void binderDied(const wp<IBinder> & /* who */) override {
- // Don't check for pid validity since we know it's already dead.
- sp<ResourceManagerService> service = mService.promote();
- if (service == nullptr) {
- ALOGW("ResourceManagerService is dead as well.");
- return;
- }
- service->removeResource(mPid, mClientId, false);
+void DeathNotifier::binderDied() {
+ // Don't check for pid validity since we know it's already dead.
+ std::shared_ptr<ResourceManagerService> service = mService.lock();
+ if (service == nullptr) {
+ ALOGW("ResourceManagerService is dead as well.");
+ return;
}
-
-private:
- wp<ResourceManagerService> mService;
- int mPid;
- int64_t mClientId;
-};
-
-} // namespace
+ service->removeResource(mPid, mClientId, false);
+}
template <typename T>
-static String8 getString(const Vector<T> &items) {
+static String8 getString(const std::vector<T> &items) {
String8 itemsStr;
for (size_t i = 0; i < items.size(); ++i) {
- itemsStr.appendFormat("%s ", items[i].toString().string());
+ itemsStr.appendFormat("%s ", toString(items[i]).string());
}
return itemsStr;
}
static bool hasResourceType(MediaResource::Type type, const ResourceList& resources) {
for (auto it = resources.begin(); it != resources.end(); it++) {
- if (it->second.mType == type) {
+ if (it->second.type == type) {
return true;
}
}
@@ -105,7 +103,7 @@
static ResourceInfo& getResourceInfoForEdit(
uid_t uid,
int64_t clientId,
- const sp<IResourceManagerClient>& client,
+ const std::shared_ptr<IResourceManagerClient>& client,
ResourceInfos& infos) {
ssize_t index = infos.indexOfKey(clientId);
@@ -121,29 +119,30 @@
return infos.editValueAt(index);
}
-static void notifyResourceGranted(int pid, const Vector<MediaResource> &resources) {
+static void notifyResourceGranted(int pid, const std::vector<MediaResourceParcel> &resources) {
static const char* const kServiceName = "media_resource_monitor";
sp<IBinder> binder = defaultServiceManager()->checkService(String16(kServiceName));
if (binder != NULL) {
sp<IMediaResourceMonitor> service = interface_cast<IMediaResourceMonitor>(binder);
for (size_t i = 0; i < resources.size(); ++i) {
- if (resources[i].mSubType == MediaResource::kAudioCodec) {
+ if (resources[i].subType == MediaResource::SubType::kAudioCodec) {
service->notifyResourceGranted(pid, IMediaResourceMonitor::TYPE_AUDIO_CODEC);
- } else if (resources[i].mSubType == MediaResource::kVideoCodec) {
+ } else if (resources[i].subType == MediaResource::SubType::kVideoCodec) {
service->notifyResourceGranted(pid, IMediaResourceMonitor::TYPE_VIDEO_CODEC);
}
}
}
}
-status_t ResourceManagerService::dump(int fd, const Vector<String16>& /* args */) {
+binder_status_t ResourceManagerService::dump(
+ int fd, const char** /*args*/, uint32_t /*numArgs*/) {
String8 result;
if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
result.format("Permission Denial: "
"can't dump ResourceManagerService from pid=%d, uid=%d\n",
- IPCThreadState::self()->getCallingPid(),
- IPCThreadState::self()->getCallingUid());
+ AIBinder_getCallingPid(),
+ AIBinder_getCallingUid());
write(fd, result.string(), result.size());
return PERMISSION_DENIED;
}
@@ -182,13 +181,18 @@
snprintf(buffer, SIZE, " Id: %lld\n", (long long)infos[j].clientId);
result.append(buffer);
- snprintf(buffer, SIZE, " Name: %s\n", infos[j].client->getName().string());
+ std::string clientName;
+ Status status = infos[j].client->getName(&clientName);
+ if (!status.isOk()) {
+ clientName = "<unknown client>";
+ }
+ snprintf(buffer, SIZE, " Name: %s\n", clientName.c_str());
result.append(buffer);
const ResourceList &resources = infos[j].resources;
result.append(" Resources:\n");
for (auto it = resources.begin(); it != resources.end(); it++) {
- snprintf(buffer, SIZE, " %s\n", it->second.toString().string());
+ snprintf(buffer, SIZE, " %s\n", toString(it->second).string());
result.append(buffer);
}
}
@@ -202,7 +206,7 @@
struct SystemCallbackImpl :
public ResourceManagerService::SystemCallbackInterface {
- SystemCallbackImpl() {}
+ SystemCallbackImpl() : mClientToken(new BBinder()) {}
virtual void noteStartVideo(int uid) override {
BatteryNotifier::getInstance().noteStartVideo(uid);
@@ -213,9 +217,8 @@
virtual void noteResetVideo() override {
BatteryNotifier::getInstance().noteResetVideo();
}
- virtual bool requestCpusetBoost(
- bool enable, const sp<IInterface> &client) override {
- return android::requestCpusetBoost(enable, client);
+ virtual bool requestCpusetBoost(bool enable) override {
+ return android::requestCpusetBoost(enable, mClientToken);
}
protected:
@@ -223,6 +226,7 @@
private:
DISALLOW_EVIL_CONSTRUCTORS(SystemCallbackImpl);
+ sp<IBinder> mClientToken;
};
ResourceManagerService::ResourceManagerService()
@@ -236,78 +240,100 @@
mServiceLog(new ServiceLog()),
mSupportsMultipleSecureCodecs(true),
mSupportsSecureWithNonSecureCodec(true),
- mCpuBoostCount(0) {
+ mCpuBoostCount(0),
+ mDeathRecipient(AIBinder_DeathRecipient_new(DeathNotifier::BinderDiedCallback)) {
mSystemCB->noteResetVideo();
}
+//static
+void ResourceManagerService::instantiate() {
+ std::shared_ptr<ResourceManagerService> service =
+ ::ndk::SharedRefBase::make<ResourceManagerService>();
+ binder_status_t status =
+ AServiceManager_addService(service->asBinder().get(), getServiceName());
+ if (status != STATUS_OK) {
+ return;
+ }
+ // TODO: mediaserver main() is already starting the thread pool,
+ // move this to mediaserver main() when other services in mediaserver
+ // are converted to ndk-platform aidl.
+ //ABinderProcess_startThreadPool();
+}
+
ResourceManagerService::~ResourceManagerService() {}
-void ResourceManagerService::config(const Vector<MediaResourcePolicy> &policies) {
+Status ResourceManagerService::config(const std::vector<MediaResourcePolicyParcel>& policies) {
String8 log = String8::format("config(%s)", getString(policies).string());
mServiceLog->add(log);
Mutex::Autolock lock(mLock);
for (size_t i = 0; i < policies.size(); ++i) {
- String8 type = policies[i].mType;
- String8 value = policies[i].mValue;
- if (type == kPolicySupportsMultipleSecureCodecs) {
+ const std::string &type = policies[i].type;
+ const std::string &value = policies[i].value;
+ if (type == MediaResourcePolicy::kPolicySupportsMultipleSecureCodecs()) {
mSupportsMultipleSecureCodecs = (value == "true");
- } else if (type == kPolicySupportsSecureWithNonSecureCodec) {
+ } else if (type == MediaResourcePolicy::kPolicySupportsSecureWithNonSecureCodec()) {
mSupportsSecureWithNonSecureCodec = (value == "true");
}
}
+ return Status::ok();
}
void ResourceManagerService::onFirstAdded(
- const MediaResource& resource, const ResourceInfo& clientInfo) {
+ const MediaResourceParcel& resource, const ResourceInfo& clientInfo) {
// first time added
- if (resource.mType == MediaResource::kCpuBoost
- && resource.mSubType == MediaResource::kUnspecifiedSubType) {
+ if (resource.type == MediaResource::Type::kCpuBoost
+ && resource.subType == MediaResource::SubType::kUnspecifiedSubType) {
// Request it on every new instance of kCpuBoost, as the media.codec
// could have died, if we only do it the first time subsequent instances
// never gets the boost.
- if (mSystemCB->requestCpusetBoost(true, this) != OK) {
+ if (mSystemCB->requestCpusetBoost(true) != OK) {
ALOGW("couldn't request cpuset boost");
}
mCpuBoostCount++;
- } else if (resource.mType == MediaResource::kBattery
- && resource.mSubType == MediaResource::kVideoCodec) {
+ } else if (resource.type == MediaResource::Type::kBattery
+ && resource.subType == MediaResource::SubType::kVideoCodec) {
mSystemCB->noteStartVideo(clientInfo.uid);
}
}
void ResourceManagerService::onLastRemoved(
- const MediaResource& resource, const ResourceInfo& clientInfo) {
- if (resource.mType == MediaResource::kCpuBoost
- && resource.mSubType == MediaResource::kUnspecifiedSubType
+ const MediaResourceParcel& resource, const ResourceInfo& clientInfo) {
+ if (resource.type == MediaResource::Type::kCpuBoost
+ && resource.subType == MediaResource::SubType::kUnspecifiedSubType
&& mCpuBoostCount > 0) {
if (--mCpuBoostCount == 0) {
- mSystemCB->requestCpusetBoost(false, this);
+ mSystemCB->requestCpusetBoost(false);
}
- } else if (resource.mType == MediaResource::kBattery
- && resource.mSubType == MediaResource::kVideoCodec) {
+ } else if (resource.type == MediaResource::Type::kBattery
+ && resource.subType == MediaResource::SubType::kVideoCodec) {
mSystemCB->noteStopVideo(clientInfo.uid);
}
}
void ResourceManagerService::mergeResources(
- MediaResource& r1, const MediaResource& r2) {
- if (r1.mType == MediaResource::kDrmSession) {
- // This means we are using a session. Each session's mValue is initialized to UINT64_MAX.
- // The oftener a session is used the lower it's mValue. During reclaim the session with
- // the highest mValue/lowest usage would be closed.
- r1.mValue -= (r1.mValue == 0 ? 0 : 1);
+ MediaResourceParcel& r1, const MediaResourceParcel& r2) {
+ // The resource entry on record is maintained to be in [0,INT64_MAX].
+ // Clamp if merging in the new resource value causes it to go out of bound.
+ // Note that the new resource value could be negative, eg.DrmSession, the
+ // value goes lower when the session is used more often. During reclaim
+ // the session with the highest value (lowest usage) would be closed.
+ if (r2.value < INT64_MAX - r1.value) {
+ r1.value += r2.value;
+ if (r1.value < 0) {
+ r1.value = 0;
+ }
} else {
- r1.mValue += r2.mValue;
+ r1.value = INT64_MAX;
}
}
-void ResourceManagerService::addResource(
- int pid,
- int uid,
+Status ResourceManagerService::addResource(
+ int32_t pid,
+ int32_t uid,
int64_t clientId,
- const sp<IResourceManagerClient> client,
- const Vector<MediaResource> &resources) {
+ const std::shared_ptr<IResourceManagerClient>& client,
+ const std::vector<MediaResourceParcel>& resources) {
String8 log = String8::format("addResource(pid %d, clientId %lld, resources %s)",
pid, (long long) clientId, getString(resources).string());
mServiceLog->add(log);
@@ -315,15 +341,26 @@
Mutex::Autolock lock(mLock);
if (!mProcessInfo->isValidPid(pid)) {
ALOGE("Rejected addResource call with invalid pid.");
- return;
+ return Status::fromServiceSpecificError(BAD_VALUE);
}
ResourceInfos& infos = getResourceInfosForEdit(pid, mMap);
ResourceInfo& info = getResourceInfoForEdit(uid, clientId, client, infos);
for (size_t i = 0; i < resources.size(); ++i) {
const auto &res = resources[i];
- const auto resType = std::tuple(res.mType, res.mSubType, res.mId);
+ const auto resType = std::tuple(res.type, res.subType, res.id);
+
+ if (res.value < 0 && res.type != MediaResource::Type::kDrmSession) {
+ ALOGW("Ignoring request to remove negative value of non-drm resource");
+ continue;
+ }
if (info.resources.find(resType) == info.resources.end()) {
+ if (res.value <= 0) {
+ // We can't init a new entry with negative value, although it's allowed
+ // to merge in negative values after the initial add.
+ ALOGW("Ignoring request to add new resource entry with value <= 0");
+ continue;
+ }
onFirstAdded(res, info);
info.resources[resType] = res;
} else {
@@ -331,14 +368,17 @@
}
}
if (info.deathNotifier == nullptr && client != nullptr) {
- info.deathNotifier = new DeathNotifier(this, pid, clientId);
- IInterface::asBinder(client)->linkToDeath(info.deathNotifier);
+ info.deathNotifier = new DeathNotifier(ref<ResourceManagerService>(), pid, clientId);
+ AIBinder_linkToDeath(client->asBinder().get(),
+ mDeathRecipient.get(), info.deathNotifier.get());
}
notifyResourceGranted(pid, resources);
+ return Status::ok();
}
-void ResourceManagerService::removeResource(int pid, int64_t clientId,
- const Vector<MediaResource> &resources) {
+Status ResourceManagerService::removeResource(
+ int32_t pid, int64_t clientId,
+ const std::vector<MediaResourceParcel>& resources) {
String8 log = String8::format("removeResource(pid %d, clientId %lld, resources %s)",
pid, (long long) clientId, getString(resources).string());
mServiceLog->add(log);
@@ -346,46 +386,51 @@
Mutex::Autolock lock(mLock);
if (!mProcessInfo->isValidPid(pid)) {
ALOGE("Rejected removeResource call with invalid pid.");
- return;
+ return Status::fromServiceSpecificError(BAD_VALUE);
}
ssize_t index = mMap.indexOfKey(pid);
if (index < 0) {
ALOGV("removeResource: didn't find pid %d for clientId %lld", pid, (long long) clientId);
- return;
+ return Status::ok();
}
ResourceInfos &infos = mMap.editValueAt(index);
index = infos.indexOfKey(clientId);
if (index < 0) {
ALOGV("removeResource: didn't find clientId %lld", (long long) clientId);
- return;
+ return Status::ok();
}
ResourceInfo &info = infos.editValueAt(index);
for (size_t i = 0; i < resources.size(); ++i) {
const auto &res = resources[i];
- const auto resType = std::tuple(res.mType, res.mSubType, res.mId);
+ const auto resType = std::tuple(res.type, res.subType, res.id);
+
+ if (res.value < 0) {
+ ALOGW("Ignoring request to remove negative value of resource");
+ continue;
+ }
// ignore if we don't have it
if (info.resources.find(resType) != info.resources.end()) {
- MediaResource &resource = info.resources[resType];
- if (resource.mValue > res.mValue) {
- resource.mValue -= res.mValue;
+ MediaResourceParcel &resource = info.resources[resType];
+ if (resource.value > res.value) {
+ resource.value -= res.value;
} else {
- // drm sessions always take this branch because res.mValue is set
- // to UINT64_MAX
onLastRemoved(res, info);
info.resources.erase(resType);
}
}
}
+ return Status::ok();
}
-void ResourceManagerService::removeClient(int pid, int64_t clientId) {
+Status ResourceManagerService::removeClient(int32_t pid, int64_t clientId) {
removeResource(pid, clientId, true);
+ return Status::ok();
}
-void ResourceManagerService::removeResource(int pid, int64_t clientId, bool checkValid) {
+Status ResourceManagerService::removeResource(int pid, int64_t clientId, bool checkValid) {
String8 log = String8::format(
"removeResource(pid %d, clientId %lld)",
pid, (long long) clientId);
@@ -394,19 +439,19 @@
Mutex::Autolock lock(mLock);
if (checkValid && !mProcessInfo->isValidPid(pid)) {
ALOGE("Rejected removeResource call with invalid pid.");
- return;
+ return Status::fromServiceSpecificError(BAD_VALUE);
}
ssize_t index = mMap.indexOfKey(pid);
if (index < 0) {
ALOGV("removeResource: didn't find pid %d for clientId %lld", pid, (long long) clientId);
- return;
+ return Status::ok();
}
ResourceInfos &infos = mMap.editValueAt(index);
index = infos.indexOfKey(clientId);
if (index < 0) {
ALOGV("removeResource: didn't find clientId %lld", (long long) clientId);
- return;
+ return Status::ok();
}
const ResourceInfo &info = infos[index];
@@ -414,48 +459,54 @@
onLastRemoved(it->second, info);
}
- IInterface::asBinder(info.client)->unlinkToDeath(info.deathNotifier);
+ AIBinder_unlinkToDeath(info.client->asBinder().get(),
+ mDeathRecipient.get(), info.deathNotifier.get());
infos.removeItemsAt(index);
+ return Status::ok();
}
void ResourceManagerService::getClientForResource_l(
- int callingPid, const MediaResource *res, Vector<sp<IResourceManagerClient>> *clients) {
+ int callingPid, const MediaResourceParcel *res,
+ Vector<std::shared_ptr<IResourceManagerClient>> *clients) {
if (res == NULL) {
return;
}
- sp<IResourceManagerClient> client;
- if (getLowestPriorityBiggestClient_l(callingPid, res->mType, &client)) {
+ std::shared_ptr<IResourceManagerClient> client;
+ if (getLowestPriorityBiggestClient_l(callingPid, res->type, &client)) {
clients->push_back(client);
}
}
-bool ResourceManagerService::reclaimResource(
- int callingPid, const Vector<MediaResource> &resources) {
+Status ResourceManagerService::reclaimResource(
+ int32_t callingPid,
+ const std::vector<MediaResourceParcel>& resources,
+ bool* _aidl_return) {
String8 log = String8::format("reclaimResource(callingPid %d, resources %s)",
callingPid, getString(resources).string());
mServiceLog->add(log);
+ *_aidl_return = false;
- Vector<sp<IResourceManagerClient>> clients;
+ Vector<std::shared_ptr<IResourceManagerClient>> clients;
{
Mutex::Autolock lock(mLock);
if (!mProcessInfo->isValidPid(callingPid)) {
ALOGE("Rejected reclaimResource call with invalid callingPid.");
- return false;
+ return Status::fromServiceSpecificError(BAD_VALUE);
}
- const MediaResource *secureCodec = NULL;
- const MediaResource *nonSecureCodec = NULL;
- const MediaResource *graphicMemory = NULL;
- const MediaResource *drmSession = NULL;
+ const MediaResourceParcel *secureCodec = NULL;
+ const MediaResourceParcel *nonSecureCodec = NULL;
+ const MediaResourceParcel *graphicMemory = NULL;
+ const MediaResourceParcel *drmSession = NULL;
for (size_t i = 0; i < resources.size(); ++i) {
- MediaResource::Type type = resources[i].mType;
- if (resources[i].mType == MediaResource::kSecureCodec) {
+ MediaResource::Type type = resources[i].type;
+ if (resources[i].type == MediaResource::Type::kSecureCodec) {
secureCodec = &resources[i];
- } else if (type == MediaResource::kNonSecureCodec) {
+ } else if (type == MediaResource::Type::kNonSecureCodec) {
nonSecureCodec = &resources[i];
- } else if (type == MediaResource::kGraphicMemory) {
+ } else if (type == MediaResource::Type::kGraphicMemory) {
graphicMemory = &resources[i];
- } else if (type == MediaResource::kDrmSession) {
+ } else if (type == MediaResource::Type::kDrmSession) {
drmSession = &resources[i];
}
}
@@ -463,27 +514,27 @@
// first pass to handle secure/non-secure codec conflict
if (secureCodec != NULL) {
if (!mSupportsMultipleSecureCodecs) {
- if (!getAllClients_l(callingPid, MediaResource::kSecureCodec, &clients)) {
- return false;
+ if (!getAllClients_l(callingPid, MediaResource::Type::kSecureCodec, &clients)) {
+ return Status::ok();
}
}
if (!mSupportsSecureWithNonSecureCodec) {
- if (!getAllClients_l(callingPid, MediaResource::kNonSecureCodec, &clients)) {
- return false;
+ if (!getAllClients_l(callingPid, MediaResource::Type::kNonSecureCodec, &clients)) {
+ return Status::ok();
}
}
}
if (nonSecureCodec != NULL) {
if (!mSupportsSecureWithNonSecureCodec) {
- if (!getAllClients_l(callingPid, MediaResource::kSecureCodec, &clients)) {
- return false;
+ if (!getAllClients_l(callingPid, MediaResource::Type::kSecureCodec, &clients)) {
+ return Status::ok();
}
}
}
if (drmSession != NULL) {
getClientForResource_l(callingPid, drmSession, &clients);
if (clients.size() == 0) {
- return false;
+ return Status::ok();
}
}
@@ -501,32 +552,35 @@
if (clients.size() == 0) {
// if we are here, run the fourth pass to free one codec with the different type.
if (secureCodec != NULL) {
- MediaResource temp(MediaResource::kNonSecureCodec, 1);
+ MediaResource temp(MediaResource::Type::kNonSecureCodec, 1);
getClientForResource_l(callingPid, &temp, &clients);
}
if (nonSecureCodec != NULL) {
- MediaResource temp(MediaResource::kSecureCodec, 1);
+ MediaResource temp(MediaResource::Type::kSecureCodec, 1);
getClientForResource_l(callingPid, &temp, &clients);
}
}
}
if (clients.size() == 0) {
- return false;
+ return Status::ok();
}
- sp<IResourceManagerClient> failedClient;
+ std::shared_ptr<IResourceManagerClient> failedClient;
for (size_t i = 0; i < clients.size(); ++i) {
log = String8::format("reclaimResource from client %p", clients[i].get());
mServiceLog->add(log);
- if (!clients[i]->reclaimResource()) {
+ bool success;
+ Status status = clients[i]->reclaimResource(&success);
+ if (!status.isOk() || !success) {
failedClient = clients[i];
break;
}
}
if (failedClient == NULL) {
- return true;
+ *_aidl_return = true;
+ return Status::ok();
}
{
@@ -551,12 +605,13 @@
}
}
- return false;
+ return Status::ok();
}
bool ResourceManagerService::getAllClients_l(
- int callingPid, MediaResource::Type type, Vector<sp<IResourceManagerClient>> *clients) {
- Vector<sp<IResourceManagerClient>> temp;
+ int callingPid, MediaResource::Type type,
+ Vector<std::shared_ptr<IResourceManagerClient>> *clients) {
+ Vector<std::shared_ptr<IResourceManagerClient>> temp;
for (size_t i = 0; i < mMap.size(); ++i) {
ResourceInfos &infos = mMap.editValueAt(i);
for (size_t j = 0; j < infos.size(); ++j) {
@@ -581,7 +636,8 @@
}
bool ResourceManagerService::getLowestPriorityBiggestClient_l(
- int callingPid, MediaResource::Type type, sp<IResourceManagerClient> *client) {
+ int callingPid, MediaResource::Type type,
+ std::shared_ptr<IResourceManagerClient> *client) {
int lowestPriorityPid;
int lowestPriority;
int callingPriority;
@@ -653,23 +709,23 @@
}
bool ResourceManagerService::getBiggestClient_l(
- int pid, MediaResource::Type type, sp<IResourceManagerClient> *client) {
+ int pid, MediaResource::Type type, std::shared_ptr<IResourceManagerClient> *client) {
ssize_t index = mMap.indexOfKey(pid);
if (index < 0) {
ALOGE("getBiggestClient_l: can't find resource info for pid %d", pid);
return false;
}
- sp<IResourceManagerClient> clientTemp;
+ std::shared_ptr<IResourceManagerClient> clientTemp;
uint64_t largestValue = 0;
const ResourceInfos &infos = mMap.valueAt(index);
for (size_t i = 0; i < infos.size(); ++i) {
const ResourceList &resources = infos[i].resources;
for (auto it = resources.begin(); it != resources.end(); it++) {
- const MediaResource &resource = it->second;
- if (resource.mType == type) {
- if (resource.mValue > largestValue) {
- largestValue = resource.mValue;
+ const MediaResourceParcel &resource = it->second;
+ if (resource.type == type) {
+ if (resource.value > largestValue) {
+ largestValue = resource.value;
clientTemp = infos[i].client;
}
}
diff --git a/services/mediaresourcemanager/ResourceManagerService.h b/services/mediaresourcemanager/ResourceManagerService.h
index 44d0c28..ae12d7b 100644
--- a/services/mediaresourcemanager/ResourceManagerService.h
+++ b/services/mediaresourcemanager/ResourceManagerService.h
@@ -15,30 +15,40 @@
** limitations under the License.
*/
-#ifndef ANDROID_RESOURCEMANAGERSERVICE_H
-#define ANDROID_RESOURCEMANAGERSERVICE_H
+#ifndef ANDROID_MEDIA_RESOURCEMANAGERSERVICE_H
+#define ANDROID_MEDIA_RESOURCEMANAGERSERVICE_H
+#include <aidl/android/media/BnResourceManagerService.h>
#include <arpa/inet.h>
-#include <binder/BinderService.h>
+#include <media/MediaResource.h>
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
#include <utils/String8.h>
#include <utils/threads.h>
#include <utils/Vector.h>
-#include <media/IResourceManagerService.h>
-
namespace android {
+class DeathNotifier;
+class ResourceManagerService;
class ServiceLog;
struct ProcessInfoInterface;
-typedef std::map<std::tuple<MediaResource::Type, MediaResource::SubType, std::vector<uint8_t>>, MediaResource> ResourceList;
+using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::media::IResourceManagerClient;
+using ::aidl::android::media::BnResourceManagerService;
+using ::aidl::android::media::MediaResourceParcel;
+using ::aidl::android::media::MediaResourcePolicyParcel;
+
+typedef std::map<std::tuple<
+ MediaResource::Type, MediaResource::SubType, std::vector<int8_t>>,
+ MediaResourceParcel> ResourceList;
+
struct ResourceInfo {
int64_t clientId;
uid_t uid;
- sp<IResourceManagerClient> client;
- sp<IBinder::DeathRecipient> deathNotifier;
+ std::shared_ptr<IResourceManagerClient> client;
+ sp<DeathNotifier> deathNotifier;
ResourceList resources;
};
@@ -46,52 +56,69 @@
typedef KeyedVector<int64_t, ResourceInfo> ResourceInfos;
typedef KeyedVector<int, ResourceInfos> PidResourceInfosMap;
-class ResourceManagerService
- : public BinderService<ResourceManagerService>,
- public BnResourceManagerService
-{
+class DeathNotifier : public RefBase {
+public:
+ DeathNotifier(const std::shared_ptr<ResourceManagerService> &service,
+ int pid, int64_t clientId);
+
+ ~DeathNotifier() {}
+
+ // Implement death recipient
+ static void BinderDiedCallback(void* cookie);
+ void binderDied();
+
+private:
+ std::weak_ptr<ResourceManagerService> mService;
+ int mPid;
+ int64_t mClientId;
+};
+class ResourceManagerService : public BnResourceManagerService {
public:
struct SystemCallbackInterface : public RefBase {
virtual void noteStartVideo(int uid) = 0;
virtual void noteStopVideo(int uid) = 0;
virtual void noteResetVideo() = 0;
- virtual bool requestCpusetBoost(
- bool enable, const sp<IInterface> &client) = 0;
+ virtual bool requestCpusetBoost(bool enable) = 0;
};
static char const *getServiceName() { return "media.resource_manager"; }
+ static void instantiate();
- virtual status_t dump(int fd, const Vector<String16>& args);
+ virtual inline binder_status_t dump(
+ int /*fd*/, const char** /*args*/, uint32_t /*numArgs*/);
ResourceManagerService();
explicit ResourceManagerService(
const sp<ProcessInfoInterface> &processInfo,
const sp<SystemCallbackInterface> &systemResource);
+ virtual ~ResourceManagerService();
// IResourceManagerService interface
- virtual void config(const Vector<MediaResourcePolicy> &policies);
+ Status config(const std::vector<MediaResourcePolicyParcel>& policies) override;
- virtual void addResource(
- int pid,
- int uid,
+ Status addResource(
+ int32_t pid,
+ int32_t uid,
int64_t clientId,
- const sp<IResourceManagerClient> client,
- const Vector<MediaResource> &resources);
+ const std::shared_ptr<IResourceManagerClient>& client,
+ const std::vector<MediaResourceParcel>& resources) override;
- virtual void removeResource(int pid, int64_t clientId,
- const Vector<MediaResource> &resources);
+ Status removeResource(
+ int32_t pid,
+ int64_t clientId,
+ const std::vector<MediaResourceParcel>& resources) override;
- virtual void removeClient(int pid, int64_t clientId);
+ Status removeClient(int32_t pid, int64_t clientId) override;
// Tries to reclaim resource from processes with lower priority than the calling process
// according to the requested resources.
// Returns true if any resource has been reclaimed, otherwise returns false.
- virtual bool reclaimResource(int callingPid, const Vector<MediaResource> &resources);
+ Status reclaimResource(
+ int32_t callingPid,
+ const std::vector<MediaResourceParcel>& resources,
+ bool* _aidl_return) override;
- void removeResource(int pid, int64_t clientId, bool checkValid);
-
-protected:
- virtual ~ResourceManagerService();
+ Status removeResource(int pid, int64_t clientId, bool checkValid);
private:
friend class ResourceManagerServiceTest;
@@ -100,13 +127,13 @@
// Returns false if any client belongs to a process with higher priority than the
// calling process. The clients will remain unchanged if returns false.
bool getAllClients_l(int callingPid, MediaResource::Type type,
- Vector<sp<IResourceManagerClient>> *clients);
+ Vector<std::shared_ptr<IResourceManagerClient>> *clients);
// Gets the client who owns specified resource type from lowest possible priority process.
// Returns false if the calling process priority is not higher than the lowest process
// priority. The client will remain unchanged if returns false.
bool getLowestPriorityBiggestClient_l(int callingPid, MediaResource::Type type,
- sp<IResourceManagerClient> *client);
+ std::shared_ptr<IResourceManagerClient> *client);
// Gets lowest priority process that has the specified resource type.
// Returns false if failed. The output parameters will remain unchanged if failed.
@@ -114,20 +141,21 @@
// Gets the client who owns biggest piece of specified resource type from pid.
// Returns false if failed. The client will remain unchanged if failed.
- bool getBiggestClient_l(int pid, MediaResource::Type type, sp<IResourceManagerClient> *client);
+ bool getBiggestClient_l(int pid, MediaResource::Type type,
+ std::shared_ptr<IResourceManagerClient> *client);
bool isCallingPriorityHigher_l(int callingPid, int pid);
- // A helper function basically calls getLowestPriorityBiggestClient_l and add the result client
- // to the given Vector.
- void getClientForResource_l(
- int callingPid, const MediaResource *res, Vector<sp<IResourceManagerClient>> *clients);
+ // A helper function basically calls getLowestPriorityBiggestClient_l and add
+ // the result client to the given Vector.
+ void getClientForResource_l(int callingPid, const MediaResourceParcel *res,
+ Vector<std::shared_ptr<IResourceManagerClient>> *clients);
- void onFirstAdded(const MediaResource& res, const ResourceInfo& clientInfo);
- void onLastRemoved(const MediaResource& res, const ResourceInfo& clientInfo);
+ void onFirstAdded(const MediaResourceParcel& res, const ResourceInfo& clientInfo);
+ void onLastRemoved(const MediaResourceParcel& res, const ResourceInfo& clientInfo);
// Merge r2 into r1
- void mergeResources(MediaResource& r1, const MediaResource& r2);
+ void mergeResources(MediaResourceParcel& r1, const MediaResourceParcel& r2);
mutable Mutex mLock;
sp<ProcessInfoInterface> mProcessInfo;
@@ -137,10 +165,10 @@
bool mSupportsMultipleSecureCodecs;
bool mSupportsSecureWithNonSecureCodec;
int32_t mCpuBoostCount;
+ ::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
};
// ----------------------------------------------------------------------------
+} // namespace android
-}; // namespace android
-
-#endif // ANDROID_RESOURCEMANAGERSERVICE_H
+#endif // ANDROID_MEDIA_RESOURCEMANAGERSERVICE_H
diff --git a/services/mediaresourcemanager/test/Android.bp b/services/mediaresourcemanager/test/Android.bp
index 543c87c..b6c548c 100644
--- a/services/mediaresourcemanager/test/Android.bp
+++ b/services/mediaresourcemanager/test/Android.bp
@@ -5,6 +5,7 @@
test_suites: ["device-tests"],
shared_libs: [
"libbinder",
+ "libbinder_ndk",
"liblog",
"libmedia",
"libresourcemanagerservice",
diff --git a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
index 9e14151..168fde9 100644
--- a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
+++ b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
@@ -21,15 +21,28 @@
#include <gtest/gtest.h>
#include "ResourceManagerService.h"
-#include <media/IResourceManagerService.h>
+#include <aidl/android/media/BnResourceManagerClient.h>
#include <media/MediaResource.h>
#include <media/MediaResourcePolicy.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/ProcessInfoInterface.h>
+namespace aidl {
+namespace android {
+namespace media {
+bool operator== (const MediaResourceParcel& lhs, const MediaResourceParcel& rhs) {
+ return lhs.type == rhs.type && lhs.subType == rhs.subType &&
+ lhs.id == rhs.id && lhs.value == rhs.value;
+}}}}
+
namespace android {
-static int64_t getId(const sp<IResourceManagerClient>& client) {
+using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::media::BnResourceManagerClient;
+using ::aidl::android::media::IResourceManagerService;
+using ::aidl::android::media::IResourceManagerClient;
+
+static int64_t getId(const std::shared_ptr<IResourceManagerClient>& client) {
return (int64_t) client.get();
}
@@ -86,8 +99,7 @@
mEventCount++;
}
- virtual bool requestCpusetBoost(
- bool enable, const sp<IInterface> &/*client*/) override {
+ virtual bool requestCpusetBoost(bool enable) override {
mLastEvent = {enable ? EventType::CPUSET_ENABLE : EventType::CPUSET_DISABLE, 0};
mEventCount++;
return true;
@@ -109,18 +121,19 @@
struct TestClient : public BnResourceManagerClient {
- TestClient(int pid, sp<ResourceManagerService> service)
+ TestClient(int pid, const std::shared_ptr<ResourceManagerService> &service)
: mReclaimed(false), mPid(pid), mService(service) {}
- virtual bool reclaimResource() {
- sp<IResourceManagerClient> client(this);
- mService->removeClient(mPid, (int64_t) client.get());
+ Status reclaimResource(bool* _aidl_return) override {
+ mService->removeClient(mPid, getId(ref<TestClient>()));
mReclaimed = true;
- return true;
+ *_aidl_return = true;
+ return Status::ok();
}
- virtual String8 getName() {
- return String8("test_client");
+ Status getName(::std::string* _aidl_return) override {
+ *_aidl_return = "test_client";
+ return Status::ok();
}
bool reclaimed() const {
@@ -131,13 +144,12 @@
mReclaimed = false;
}
-protected:
virtual ~TestClient() {}
private:
bool mReclaimed;
int mPid;
- sp<ResourceManagerService> mService;
+ std::shared_ptr<ResourceManagerService> mService;
DISALLOW_EVIL_CONSTRUCTORS(TestClient);
};
@@ -157,24 +169,31 @@
return lhs.type == rhs.type && lhs.arg == rhs.arg;
}
+#define CHECK_STATUS_TRUE(condition) \
+ EXPECT_TRUE((condition).isOk() && (result))
+
+#define CHECK_STATUS_FALSE(condition) \
+ EXPECT_TRUE((condition).isOk() && !(result))
+
class ResourceManagerServiceTest : public ::testing::Test {
public:
ResourceManagerServiceTest()
: mSystemCB(new TestSystemCallback()),
- mService(new ResourceManagerService(new TestProcessInfo, mSystemCB)),
- mTestClient1(new TestClient(kTestPid1, mService)),
- mTestClient2(new TestClient(kTestPid2, mService)),
- mTestClient3(new TestClient(kTestPid2, mService)) {
+ mService(::ndk::SharedRefBase::make<ResourceManagerService>(
+ new TestProcessInfo, mSystemCB)),
+ mTestClient1(::ndk::SharedRefBase::make<TestClient>(kTestPid1, mService)),
+ mTestClient2(::ndk::SharedRefBase::make<TestClient>(kTestPid2, mService)),
+ mTestClient3(::ndk::SharedRefBase::make<TestClient>(kTestPid2, mService)) {
}
protected:
- static bool isEqualResources(const Vector<MediaResource> &resources1,
+ static bool isEqualResources(const std::vector<MediaResourceParcel> &resources1,
const ResourceList &resources2) {
// convert resource1 to ResourceList
ResourceList r1;
for (size_t i = 0; i < resources1.size(); ++i) {
const auto &res = resources1[i];
- const auto resType = std::tuple(res.mType, res.mSubType, res.mId);
+ const auto resType = std::tuple(res.type, res.subType, res.id);
r1[resType] = res;
}
return r1 == resources2;
@@ -182,8 +201,8 @@
static void expectEqResourceInfo(const ResourceInfo &info,
int uid,
- sp<IResourceManagerClient> client,
- const Vector<MediaResource> &resources) {
+ std::shared_ptr<IResourceManagerClient> client,
+ const std::vector<MediaResourceParcel> &resources) {
EXPECT_EQ(uid, info.uid);
EXPECT_EQ(client, info.client);
EXPECT_TRUE(isEqualResources(resources, info.resources));
@@ -219,25 +238,25 @@
// ---------------------------------------------------------------------------------
void addResource() {
// kTestPid1 mTestClient1
- Vector<MediaResource> resources1;
- resources1.push_back(MediaResource(MediaResource::kSecureCodec, 1));
+ std::vector<MediaResourceParcel> resources1;
+ resources1.push_back(MediaResource(MediaResource::Type::kSecureCodec, 1));
mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources1);
- resources1.push_back(MediaResource(MediaResource::kGraphicMemory, 200));
- Vector<MediaResource> resources11;
- resources11.push_back(MediaResource(MediaResource::kGraphicMemory, 200));
+ resources1.push_back(MediaResource(MediaResource::Type::kGraphicMemory, 200));
+ std::vector<MediaResourceParcel> resources11;
+ resources11.push_back(MediaResource(MediaResource::Type::kGraphicMemory, 200));
mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources11);
// kTestPid2 mTestClient2
- Vector<MediaResource> resources2;
- resources2.push_back(MediaResource(MediaResource::kNonSecureCodec, 1));
- resources2.push_back(MediaResource(MediaResource::kGraphicMemory, 300));
+ std::vector<MediaResourceParcel> resources2;
+ resources2.push_back(MediaResource(MediaResource::Type::kNonSecureCodec, 1));
+ resources2.push_back(MediaResource(MediaResource::Type::kGraphicMemory, 300));
mService->addResource(kTestPid2, kTestUid2, getId(mTestClient2), mTestClient2, resources2);
// kTestPid2 mTestClient3
- Vector<MediaResource> resources3;
+ std::vector<MediaResourceParcel> resources3;
mService->addResource(kTestPid2, kTestUid2, getId(mTestClient3), mTestClient3, resources3);
- resources3.push_back(MediaResource(MediaResource::kSecureCodec, 1));
- resources3.push_back(MediaResource(MediaResource::kGraphicMemory, 100));
+ resources3.push_back(MediaResource(MediaResource::Type::kSecureCodec, 1));
+ resources3.push_back(MediaResource(MediaResource::Type::kGraphicMemory, 100));
mService->addResource(kTestPid2, kTestUid2, getId(mTestClient3), mTestClient3, resources3);
const PidResourceInfosMap &map = mService->mMap;
@@ -256,32 +275,92 @@
expectEqResourceInfo(infos2.valueFor(getId(mTestClient3)), kTestUid2, mTestClient3, resources3);
}
+ void testCombineResourceWithNegativeValues() {
+ // kTestPid1 mTestClient1
+ std::vector<MediaResourceParcel> resources1;
+ resources1.push_back(MediaResource(MediaResource::Type::kDrmSession, -100));
+ resources1.push_back(MediaResource(MediaResource::Type::kNonSecureCodec, -100));
+ mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources1);
+
+ // Expected result:
+ // 1) the client should have been added;
+ // 2) both resource entries should have been rejected, resource list should be empty.
+ const PidResourceInfosMap &map = mService->mMap;
+ EXPECT_EQ(1u, map.size());
+ ssize_t index1 = map.indexOfKey(kTestPid1);
+ ASSERT_GE(index1, 0);
+ const ResourceInfos &infos1 = map[index1];
+ EXPECT_EQ(1u, infos1.size());
+ std::vector<MediaResourceParcel> expected;
+ expectEqResourceInfo(infos1.valueFor(getId(mTestClient1)), kTestUid1, mTestClient1, expected);
+
+ resources1.clear();
+ resources1.push_back(MediaResource(MediaResource::Type::kDrmSession, INT64_MAX));
+ resources1.push_back(MediaResource(MediaResource::Type::kNonSecureCodec, INT64_MAX));
+ mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources1);
+ resources1.clear();
+ resources1.push_back(MediaResource(MediaResource::Type::kDrmSession, 10));
+ resources1.push_back(MediaResource(MediaResource::Type::kNonSecureCodec, 10));
+ mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources1);
+
+ // Expected result:
+ // Both values should saturate to INT64_MAX
+ expected.push_back(MediaResource(MediaResource::Type::kDrmSession, INT64_MAX));
+ expected.push_back(MediaResource(MediaResource::Type::kNonSecureCodec, INT64_MAX));
+ expectEqResourceInfo(infos1.valueFor(getId(mTestClient1)), kTestUid1, mTestClient1, expected);
+
+ resources1.clear();
+ resources1.push_back(MediaResource(MediaResource::Type::kDrmSession, -10));
+ resources1.push_back(MediaResource(MediaResource::Type::kNonSecureCodec, -10));
+ mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources1);
+
+ // Expected result:
+ // 1) DrmSession resource should allow negative value addition, and value should drop accordingly
+ // 2) Non-drm session resource should ignore negative value addition.
+ expected.push_back(MediaResource(MediaResource::Type::kDrmSession, INT64_MAX - 10));
+ expected.push_back(MediaResource(MediaResource::Type::kNonSecureCodec, INT64_MAX));
+ expectEqResourceInfo(infos1.valueFor(getId(mTestClient1)), kTestUid1, mTestClient1, expected);
+
+ resources1.clear();
+ resources1.push_back(MediaResource(MediaResource::Type::kDrmSession, INT64_MIN));
+ expected.push_back(MediaResource(MediaResource::Type::kNonSecureCodec, INT64_MIN));
+ mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources1);
+
+ // Expected result:
+ // 1) DrmSession resource value should drop to 0, but the entry shouldn't be removed.
+ // 2) Non-drm session resource should ignore negative value addition.
+ expected.clear();
+ expected.push_back(MediaResource(MediaResource::Type::kDrmSession, 0));
+ expected.push_back(MediaResource(MediaResource::Type::kNonSecureCodec, INT64_MAX));
+ expectEqResourceInfo(infos1.valueFor(getId(mTestClient1)), kTestUid1, mTestClient1, expected);
+ }
+
void testConfig() {
EXPECT_TRUE(mService->mSupportsMultipleSecureCodecs);
EXPECT_TRUE(mService->mSupportsSecureWithNonSecureCodec);
- Vector<MediaResourcePolicy> policies1;
+ std::vector<MediaResourcePolicyParcel> policies1;
policies1.push_back(
MediaResourcePolicy(
- String8(kPolicySupportsMultipleSecureCodecs),
- String8("true")));
+ IResourceManagerService::kPolicySupportsMultipleSecureCodecs,
+ "true"));
policies1.push_back(
MediaResourcePolicy(
- String8(kPolicySupportsSecureWithNonSecureCodec),
- String8("false")));
+ IResourceManagerService::kPolicySupportsSecureWithNonSecureCodec,
+ "false"));
mService->config(policies1);
EXPECT_TRUE(mService->mSupportsMultipleSecureCodecs);
EXPECT_FALSE(mService->mSupportsSecureWithNonSecureCodec);
- Vector<MediaResourcePolicy> policies2;
+ std::vector<MediaResourcePolicyParcel> policies2;
policies2.push_back(
MediaResourcePolicy(
- String8(kPolicySupportsMultipleSecureCodecs),
- String8("false")));
+ IResourceManagerService::kPolicySupportsMultipleSecureCodecs,
+ "false"));
policies2.push_back(
MediaResourcePolicy(
- String8(kPolicySupportsSecureWithNonSecureCodec),
- String8("true")));
+ IResourceManagerService::kPolicySupportsSecureWithNonSecureCodec,
+ "true"));
mService->config(policies2);
EXPECT_FALSE(mService->mSupportsMultipleSecureCodecs);
EXPECT_TRUE(mService->mSupportsSecureWithNonSecureCodec);
@@ -289,12 +368,12 @@
void testCombineResource() {
// kTestPid1 mTestClient1
- Vector<MediaResource> resources1;
- resources1.push_back(MediaResource(MediaResource::kSecureCodec, 1));
+ std::vector<MediaResourceParcel> resources1;
+ resources1.push_back(MediaResource(MediaResource::Type::kSecureCodec, 1));
mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources1);
- Vector<MediaResource> resources11;
- resources11.push_back(MediaResource(MediaResource::kGraphicMemory, 200));
+ std::vector<MediaResourceParcel> resources11;
+ resources11.push_back(MediaResource(MediaResource::Type::kGraphicMemory, 200));
mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources11);
const PidResourceInfosMap &map = mService->mMap;
@@ -305,35 +384,35 @@
EXPECT_EQ(1u, infos1.size());
// test adding existing types to combine values
- resources1.push_back(MediaResource(MediaResource::kGraphicMemory, 100));
+ resources1.push_back(MediaResource(MediaResource::Type::kGraphicMemory, 100));
mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources1);
- Vector<MediaResource> expected;
- expected.push_back(MediaResource(MediaResource::kSecureCodec, 2));
- expected.push_back(MediaResource(MediaResource::kGraphicMemory, 300));
+ std::vector<MediaResourceParcel> expected;
+ expected.push_back(MediaResource(MediaResource::Type::kSecureCodec, 2));
+ expected.push_back(MediaResource(MediaResource::Type::kGraphicMemory, 300));
expectEqResourceInfo(infos1.valueFor(getId(mTestClient1)), kTestUid1, mTestClient1, expected);
// test adding new types (including types that differs only in subType)
- resources11.push_back(MediaResource(MediaResource::kNonSecureCodec, 1));
- resources11.push_back(MediaResource(MediaResource::kSecureCodec, MediaResource::kVideoCodec, 1));
+ resources11.push_back(MediaResource(MediaResource::Type::kNonSecureCodec, 1));
+ resources11.push_back(MediaResource(MediaResource::Type::kSecureCodec, MediaResource::SubType::kVideoCodec, 1));
mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources11);
expected.clear();
- expected.push_back(MediaResource(MediaResource::kSecureCodec, 2));
- expected.push_back(MediaResource(MediaResource::kNonSecureCodec, 1));
- expected.push_back(MediaResource(MediaResource::kSecureCodec, MediaResource::kVideoCodec, 1));
- expected.push_back(MediaResource(MediaResource::kGraphicMemory, 500));
+ expected.push_back(MediaResource(MediaResource::Type::kSecureCodec, 2));
+ expected.push_back(MediaResource(MediaResource::Type::kNonSecureCodec, 1));
+ expected.push_back(MediaResource(MediaResource::Type::kSecureCodec, MediaResource::SubType::kVideoCodec, 1));
+ expected.push_back(MediaResource(MediaResource::Type::kGraphicMemory, 500));
expectEqResourceInfo(infos1.valueFor(getId(mTestClient1)), kTestUid1, mTestClient1, expected);
}
void testRemoveResource() {
// kTestPid1 mTestClient1
- Vector<MediaResource> resources1;
- resources1.push_back(MediaResource(MediaResource::kSecureCodec, 1));
+ std::vector<MediaResourceParcel> resources1;
+ resources1.push_back(MediaResource(MediaResource::Type::kSecureCodec, 1));
mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources1);
- Vector<MediaResource> resources11;
- resources11.push_back(MediaResource(MediaResource::kGraphicMemory, 200));
+ std::vector<MediaResourceParcel> resources11;
+ resources11.push_back(MediaResource(MediaResource::Type::kGraphicMemory, 200));
mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources11);
const PidResourceInfosMap &map = mService->mMap;
@@ -344,20 +423,26 @@
EXPECT_EQ(1u, infos1.size());
// test partial removal
- resources11.editItemAt(0).mValue = 100;
+ resources11[0].value = 100;
mService->removeResource(kTestPid1, getId(mTestClient1), resources11);
- Vector<MediaResource> expected;
- expected.push_back(MediaResource(MediaResource::kSecureCodec, 1));
- expected.push_back(MediaResource(MediaResource::kGraphicMemory, 100));
+ std::vector<MediaResourceParcel> expected;
+ expected.push_back(MediaResource(MediaResource::Type::kSecureCodec, 1));
+ expected.push_back(MediaResource(MediaResource::Type::kGraphicMemory, 100));
+ expectEqResourceInfo(infos1.valueFor(getId(mTestClient1)), kTestUid1, mTestClient1, expected);
+
+ // test removal request with negative value, should be ignored
+ resources11[0].value = -10000;
+ mService->removeResource(kTestPid1, getId(mTestClient1), resources11);
+
expectEqResourceInfo(infos1.valueFor(getId(mTestClient1)), kTestUid1, mTestClient1, expected);
// test complete removal with overshoot value
- resources11.editItemAt(0).mValue = 1000;
+ resources11[0].value = 1000;
mService->removeResource(kTestPid1, getId(mTestClient1), resources11);
expected.clear();
- expected.push_back(MediaResource(MediaResource::kSecureCodec, 1));
+ expected.push_back(MediaResource(MediaResource::Type::kSecureCodec, 1));
expectEqResourceInfo(infos1.valueFor(getId(mTestClient1)), kTestUid1, mTestClient1, expected);
}
@@ -380,8 +465,8 @@
void testGetAllClients() {
addResource();
- MediaResource::Type type = MediaResource::kSecureCodec;
- Vector<sp<IResourceManagerClient> > clients;
+ MediaResource::Type type = MediaResource::Type::kSecureCodec;
+ Vector<std::shared_ptr<IResourceManagerClient> > clients;
EXPECT_FALSE(mService->getAllClients_l(kLowPriorityPid, type, &clients));
// some higher priority process (e.g. kTestPid2) owns the resource, so getAllClients_l
// will fail.
@@ -395,9 +480,10 @@
}
void testReclaimResourceSecure() {
- Vector<MediaResource> resources;
- resources.push_back(MediaResource(MediaResource::kSecureCodec, 1));
- resources.push_back(MediaResource(MediaResource::kGraphicMemory, 150));
+ bool result;
+ std::vector<MediaResourceParcel> resources;
+ resources.push_back(MediaResource(MediaResource::Type::kSecureCodec, 1));
+ resources.push_back(MediaResource(MediaResource::Type::kGraphicMemory, 150));
// ### secure codec can't coexist and secure codec can coexist with non-secure codec ###
{
@@ -406,19 +492,19 @@
mService->mSupportsSecureWithNonSecureCodec = true;
// priority too low
- EXPECT_FALSE(mService->reclaimResource(kLowPriorityPid, resources));
- EXPECT_FALSE(mService->reclaimResource(kMidPriorityPid, resources));
+ CHECK_STATUS_FALSE(mService->reclaimResource(kLowPriorityPid, resources, &result));
+ CHECK_STATUS_FALSE(mService->reclaimResource(kMidPriorityPid, resources, &result));
// reclaim all secure codecs
- EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ CHECK_STATUS_TRUE(mService->reclaimResource(kHighPriorityPid, resources, &result));
verifyClients(true /* c1 */, false /* c2 */, true /* c3 */);
// call again should reclaim one largest graphic memory from lowest process
- EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ CHECK_STATUS_TRUE(mService->reclaimResource(kHighPriorityPid, resources, &result));
verifyClients(false /* c1 */, true /* c2 */, false /* c3 */);
// nothing left
- EXPECT_FALSE(mService->reclaimResource(kHighPriorityPid, resources));
+ CHECK_STATUS_FALSE(mService->reclaimResource(kHighPriorityPid, resources, &result));
}
// ### secure codecs can't coexist and secure codec can't coexist with non-secure codec ###
@@ -428,15 +514,15 @@
mService->mSupportsSecureWithNonSecureCodec = false;
// priority too low
- EXPECT_FALSE(mService->reclaimResource(kLowPriorityPid, resources));
- EXPECT_FALSE(mService->reclaimResource(kMidPriorityPid, resources));
+ CHECK_STATUS_FALSE(mService->reclaimResource(kLowPriorityPid, resources, &result));
+ CHECK_STATUS_FALSE(mService->reclaimResource(kMidPriorityPid, resources, &result));
// reclaim all secure and non-secure codecs
- EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ CHECK_STATUS_TRUE(mService->reclaimResource(kHighPriorityPid, resources, &result));
verifyClients(true /* c1 */, true /* c2 */, true /* c3 */);
// nothing left
- EXPECT_FALSE(mService->reclaimResource(kHighPriorityPid, resources));
+ CHECK_STATUS_FALSE(mService->reclaimResource(kHighPriorityPid, resources, &result));
}
@@ -447,23 +533,23 @@
mService->mSupportsSecureWithNonSecureCodec = false;
// priority too low
- EXPECT_FALSE(mService->reclaimResource(kLowPriorityPid, resources));
- EXPECT_FALSE(mService->reclaimResource(kMidPriorityPid, resources));
+ CHECK_STATUS_FALSE(mService->reclaimResource(kLowPriorityPid, resources, &result));
+ CHECK_STATUS_FALSE(mService->reclaimResource(kMidPriorityPid, resources, &result));
// reclaim all non-secure codecs
- EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ CHECK_STATUS_TRUE(mService->reclaimResource(kHighPriorityPid, resources, &result));
verifyClients(false /* c1 */, true /* c2 */, false /* c3 */);
// call again should reclaim one largest graphic memory from lowest process
- EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ CHECK_STATUS_TRUE(mService->reclaimResource(kHighPriorityPid, resources, &result));
verifyClients(true /* c1 */, false /* c2 */, false /* c3 */);
// call again should reclaim another largest graphic memory from lowest process
- EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ CHECK_STATUS_TRUE(mService->reclaimResource(kHighPriorityPid, resources, &result));
verifyClients(false /* c1 */, false /* c2 */, true /* c3 */);
// nothing left
- EXPECT_FALSE(mService->reclaimResource(kHighPriorityPid, resources));
+ CHECK_STATUS_FALSE(mService->reclaimResource(kHighPriorityPid, resources, &result));
}
// ### secure codecs can coexist and secure codec can coexist with non-secure codec ###
@@ -473,22 +559,22 @@
mService->mSupportsSecureWithNonSecureCodec = true;
// priority too low
- EXPECT_FALSE(mService->reclaimResource(kLowPriorityPid, resources));
+ CHECK_STATUS_FALSE(mService->reclaimResource(kLowPriorityPid, resources, &result));
- EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ CHECK_STATUS_TRUE(mService->reclaimResource(kHighPriorityPid, resources, &result));
// one largest graphic memory from lowest process got reclaimed
verifyClients(true /* c1 */, false /* c2 */, false /* c3 */);
// call again should reclaim another graphic memory from lowest process
- EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ CHECK_STATUS_TRUE(mService->reclaimResource(kHighPriorityPid, resources, &result));
verifyClients(false /* c1 */, true /* c2 */, false /* c3 */);
// call again should reclaim another graphic memory from lowest process
- EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ CHECK_STATUS_TRUE(mService->reclaimResource(kHighPriorityPid, resources, &result));
verifyClients(false /* c1 */, false /* c2 */, true /* c3 */);
// nothing left
- EXPECT_FALSE(mService->reclaimResource(kHighPriorityPid, resources));
+ CHECK_STATUS_FALSE(mService->reclaimResource(kHighPriorityPid, resources, &result));
}
// ### secure codecs can coexist and secure codec can coexist with non-secure codec ###
@@ -497,27 +583,28 @@
mService->mSupportsMultipleSecureCodecs = true;
mService->mSupportsSecureWithNonSecureCodec = true;
- Vector<MediaResource> resources;
- resources.push_back(MediaResource(MediaResource::kSecureCodec, 1));
+ std::vector<MediaResourceParcel> resources;
+ resources.push_back(MediaResource(MediaResource::Type::kSecureCodec, 1));
- EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ CHECK_STATUS_TRUE(mService->reclaimResource(kHighPriorityPid, resources, &result));
// secure codec from lowest process got reclaimed
verifyClients(true /* c1 */, false /* c2 */, false /* c3 */);
// call again should reclaim another secure codec from lowest process
- EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ CHECK_STATUS_TRUE(mService->reclaimResource(kHighPriorityPid, resources, &result));
verifyClients(false /* c1 */, false /* c2 */, true /* c3 */);
// no more secure codec, non-secure codec will be reclaimed.
- EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ CHECK_STATUS_TRUE(mService->reclaimResource(kHighPriorityPid, resources, &result));
verifyClients(false /* c1 */, true /* c2 */, false /* c3 */);
}
}
void testReclaimResourceNonSecure() {
- Vector<MediaResource> resources;
- resources.push_back(MediaResource(MediaResource::kNonSecureCodec, 1));
- resources.push_back(MediaResource(MediaResource::kGraphicMemory, 150));
+ bool result;
+ std::vector<MediaResourceParcel> resources;
+ resources.push_back(MediaResource(MediaResource::Type::kNonSecureCodec, 1));
+ resources.push_back(MediaResource(MediaResource::Type::kGraphicMemory, 150));
// ### secure codec can't coexist with non-secure codec ###
{
@@ -525,19 +612,19 @@
mService->mSupportsSecureWithNonSecureCodec = false;
// priority too low
- EXPECT_FALSE(mService->reclaimResource(kLowPriorityPid, resources));
- EXPECT_FALSE(mService->reclaimResource(kMidPriorityPid, resources));
+ CHECK_STATUS_FALSE(mService->reclaimResource(kLowPriorityPid, resources, &result));
+ CHECK_STATUS_FALSE(mService->reclaimResource(kMidPriorityPid, resources, &result));
// reclaim all secure codecs
- EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ CHECK_STATUS_TRUE(mService->reclaimResource(kHighPriorityPid, resources, &result));
verifyClients(true /* c1 */, false /* c2 */, true /* c3 */);
// call again should reclaim one graphic memory from lowest process
- EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ CHECK_STATUS_TRUE(mService->reclaimResource(kHighPriorityPid, resources, &result));
verifyClients(false /* c1 */, true /* c2 */, false /* c3 */);
// nothing left
- EXPECT_FALSE(mService->reclaimResource(kHighPriorityPid, resources));
+ CHECK_STATUS_FALSE(mService->reclaimResource(kHighPriorityPid, resources, &result));
}
@@ -547,22 +634,22 @@
mService->mSupportsSecureWithNonSecureCodec = true;
// priority too low
- EXPECT_FALSE(mService->reclaimResource(kLowPriorityPid, resources));
+ CHECK_STATUS_FALSE(mService->reclaimResource(kLowPriorityPid, resources, &result));
- EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ CHECK_STATUS_TRUE(mService->reclaimResource(kHighPriorityPid, resources, &result));
// one largest graphic memory from lowest process got reclaimed
verifyClients(true /* c1 */, false /* c2 */, false /* c3 */);
// call again should reclaim another graphic memory from lowest process
- EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ CHECK_STATUS_TRUE(mService->reclaimResource(kHighPriorityPid, resources, &result));
verifyClients(false /* c1 */, true /* c2 */, false /* c3 */);
// call again should reclaim another graphic memory from lowest process
- EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ CHECK_STATUS_TRUE(mService->reclaimResource(kHighPriorityPid, resources, &result));
verifyClients(false /* c1 */, false /* c2 */, true /* c3 */);
// nothing left
- EXPECT_FALSE(mService->reclaimResource(kHighPriorityPid, resources));
+ CHECK_STATUS_FALSE(mService->reclaimResource(kHighPriorityPid, resources, &result));
}
// ### secure codec can coexist with non-secure codec ###
@@ -570,15 +657,15 @@
addResource();
mService->mSupportsSecureWithNonSecureCodec = true;
- Vector<MediaResource> resources;
- resources.push_back(MediaResource(MediaResource::kNonSecureCodec, 1));
+ std::vector<MediaResourceParcel> resources;
+ resources.push_back(MediaResource(MediaResource::Type::kNonSecureCodec, 1));
- EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ CHECK_STATUS_TRUE(mService->reclaimResource(kHighPriorityPid, resources, &result));
// one non secure codec from lowest process got reclaimed
verifyClients(false /* c1 */, true /* c2 */, false /* c3 */);
// no more non-secure codec, secure codec from lowest priority process will be reclaimed
- EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ CHECK_STATUS_TRUE(mService->reclaimResource(kHighPriorityPid, resources, &result));
verifyClients(true /* c1 */, false /* c2 */, false /* c3 */);
// clean up client 3 which still left
@@ -587,8 +674,8 @@
}
void testGetLowestPriorityBiggestClient() {
- MediaResource::Type type = MediaResource::kGraphicMemory;
- sp<IResourceManagerClient> client;
+ MediaResource::Type type = MediaResource::Type::kGraphicMemory;
+ std::shared_ptr<IResourceManagerClient> client;
EXPECT_FALSE(mService->getLowestPriorityBiggestClient_l(kHighPriorityPid, type, &client));
addResource();
@@ -596,8 +683,8 @@
EXPECT_FALSE(mService->getLowestPriorityBiggestClient_l(kLowPriorityPid, type, &client));
EXPECT_TRUE(mService->getLowestPriorityBiggestClient_l(kHighPriorityPid, type, &client));
- // kTestPid1 is the lowest priority process with MediaResource::kGraphicMemory.
- // mTestClient1 has the largest MediaResource::kGraphicMemory within kTestPid1.
+ // kTestPid1 is the lowest priority process with MediaResource::Type::kGraphicMemory.
+ // mTestClient1 has the largest MediaResource::Type::kGraphicMemory within kTestPid1.
EXPECT_EQ(mTestClient1, client);
}
@@ -606,7 +693,7 @@
int priority;
TestProcessInfo processInfo;
- MediaResource::Type type = MediaResource::kGraphicMemory;
+ MediaResource::Type type = MediaResource::Type::kGraphicMemory;
EXPECT_FALSE(mService->getLowestPriorityPid_l(type, &pid, &priority));
addResource();
@@ -617,7 +704,7 @@
processInfo.getPriority(kTestPid1, &priority1);
EXPECT_EQ(priority1, priority);
- type = MediaResource::kNonSecureCodec;
+ type = MediaResource::Type::kNonSecureCodec;
EXPECT_TRUE(mService->getLowestPriorityPid_l(type, &pid, &priority));
EXPECT_EQ(kTestPid2, pid);
int priority2;
@@ -626,8 +713,8 @@
}
void testGetBiggestClient() {
- MediaResource::Type type = MediaResource::kGraphicMemory;
- sp<IResourceManagerClient> client;
+ MediaResource::Type type = MediaResource::Type::kGraphicMemory;
+ std::shared_ptr<IResourceManagerClient> client;
EXPECT_FALSE(mService->getBiggestClient_l(kTestPid2, type, &client));
addResource();
@@ -648,8 +735,8 @@
EXPECT_EQ(EventType::VIDEO_RESET, mSystemCB->lastEventType());
// new client request should cause VIDEO_ON
- Vector<MediaResource> resources1;
- resources1.push_back(MediaResource(MediaResource::kBattery, MediaResource::kVideoCodec, 1));
+ std::vector<MediaResourceParcel> resources1;
+ resources1.push_back(MediaResource(MediaResource::Type::kBattery, MediaResource::SubType::kVideoCodec, 1));
mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources1);
EXPECT_EQ(2u, mSystemCB->eventCount());
EXPECT_EQ(EventEntry({EventType::VIDEO_ON, kTestUid1}), mSystemCB->lastEvent());
@@ -659,8 +746,8 @@
EXPECT_EQ(2u, mSystemCB->eventCount());
// new client request should cause VIDEO_ON
- Vector<MediaResource> resources2;
- resources2.push_back(MediaResource(MediaResource::kBattery, MediaResource::kVideoCodec, 2));
+ std::vector<MediaResourceParcel> resources2;
+ resources2.push_back(MediaResource(MediaResource::Type::kBattery, MediaResource::SubType::kVideoCodec, 2));
mService->addResource(kTestPid2, kTestUid2, getId(mTestClient2), mTestClient2, resources2);
EXPECT_EQ(3u, mSystemCB->eventCount());
EXPECT_EQ(EventEntry({EventType::VIDEO_ON, kTestUid2}), mSystemCB->lastEvent());
@@ -687,8 +774,8 @@
EXPECT_EQ(EventType::VIDEO_RESET, mSystemCB->lastEventType());
// new client request should cause CPUSET_ENABLE
- Vector<MediaResource> resources1;
- resources1.push_back(MediaResource(MediaResource::kCpuBoost, 1));
+ std::vector<MediaResourceParcel> resources1;
+ resources1.push_back(MediaResource(MediaResource::Type::kCpuBoost, 1));
mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources1);
EXPECT_EQ(2u, mSystemCB->eventCount());
EXPECT_EQ(EventType::CPUSET_ENABLE, mSystemCB->lastEventType());
@@ -698,8 +785,8 @@
EXPECT_EQ(2u, mSystemCB->eventCount());
// new client request should cause CPUSET_ENABLE
- Vector<MediaResource> resources2;
- resources2.push_back(MediaResource(MediaResource::kCpuBoost, 2));
+ std::vector<MediaResourceParcel> resources2;
+ resources2.push_back(MediaResource(MediaResource::Type::kCpuBoost, 2));
mService->addResource(kTestPid2, kTestUid2, getId(mTestClient2), mTestClient2, resources2);
EXPECT_EQ(3u, mSystemCB->eventCount());
EXPECT_EQ(EventType::CPUSET_ENABLE, mSystemCB->lastEventType());
@@ -720,10 +807,10 @@
}
sp<TestSystemCallback> mSystemCB;
- sp<ResourceManagerService> mService;
- sp<IResourceManagerClient> mTestClient1;
- sp<IResourceManagerClient> mTestClient2;
- sp<IResourceManagerClient> mTestClient3;
+ std::shared_ptr<ResourceManagerService> mService;
+ std::shared_ptr<IResourceManagerClient> mTestClient1;
+ std::shared_ptr<IResourceManagerClient> mTestClient2;
+ std::shared_ptr<IResourceManagerClient> mTestClient3;
};
TEST_F(ResourceManagerServiceTest, config) {
@@ -738,6 +825,10 @@
testCombineResource();
}
+TEST_F(ResourceManagerServiceTest, combineResourceNegative) {
+ testCombineResourceWithNegativeValues();
+}
+
TEST_F(ResourceManagerServiceTest, removeResource) {
testRemoveResource();
}
diff --git a/services/mediatranscoding/.clang-format b/services/mediatranscoding/.clang-format
new file mode 100644
index 0000000..f14cc88
--- /dev/null
+++ b/services/mediatranscoding/.clang-format
@@ -0,0 +1,33 @@
+---
+BasedOnStyle: Google
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: true
+AllowShortLoopsOnASingleLine: true
+BinPackArguments: true
+BinPackParameters: true
+CommentPragmas: NOLINT:.*
+ContinuationIndentWidth: 8
+DerivePointerAlignment: false
+IndentWidth: 4
+PointerAlignment: Left
+TabWidth: 4
+
+# Deviations from the above file:
+# "Don't indent the section label"
+AccessModifierOffset: -4
+# "Each line of text in your code should be at most 100 columns long."
+ColumnLimit: 100
+# "Constructor initializer lists can be all on one line or with subsequent
+# lines indented eight spaces.". clang-format does not support having the colon
+# on the same line as the constructor function name, so this is the best
+# approximation of that rule, which makes all entries in the list (except the
+# first one) have an eight space indentation.
+ConstructorInitializerIndentWidth: 6
+# There is nothing in go/droidcppstyle about case labels, but there seems to be
+# more code that does not indent the case labels in frameworks/base.
+IndentCaseLabels: false
+# There have been some bugs in which subsequent formatting operations introduce
+# weird comment jumps.
+ReflowComments: false
+# Android does support C++11 now.
+Standard: Cpp11
\ No newline at end of file
diff --git a/services/mediatranscoding/Android.bp b/services/mediatranscoding/Android.bp
new file mode 100644
index 0000000..3dc43f1
--- /dev/null
+++ b/services/mediatranscoding/Android.bp
@@ -0,0 +1,60 @@
+// service library
+cc_library_shared {
+ name: "libmediatranscodingservice",
+
+ srcs: ["MediaTranscodingService.cpp"],
+
+ shared_libs: [
+ "libbinder_ndk",
+ "liblog",
+ ],
+
+ static_libs: [
+ "mediatranscoding_aidl_interface-ndk_platform",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+}
+
+cc_binary {
+ name: "mediatranscoding",
+
+ srcs: [
+ "main_mediatranscodingservice.cpp",
+ ],
+
+ shared_libs: [
+ // TODO(hkuang): Use libbinder_ndk
+ "libbinder",
+ "libutils",
+ "liblog",
+ "libbase",
+ "libmediatranscodingservice",
+ ],
+
+ static_libs: [
+ "mediatranscoding_aidl_interface-ndk_platform",
+ ],
+
+ target: {
+ android: {
+ product_variables: {
+ malloc_not_svelte: {
+ // Scudo increases memory footprint, so only enable on
+ // non-svelte devices.
+ shared_libs: ["libc_scudo"],
+ },
+ },
+ },
+ },
+
+ init_rc: ["mediatranscoding.rc"],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+}
diff --git a/services/mediatranscoding/MODULE_LICENSE_APACHE2 b/services/mediatranscoding/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/services/mediatranscoding/MODULE_LICENSE_APACHE2
diff --git a/services/mediatranscoding/MediaTranscodingService.cpp b/services/mediatranscoding/MediaTranscodingService.cpp
new file mode 100644
index 0000000..2680cee
--- /dev/null
+++ b/services/mediatranscoding/MediaTranscodingService.cpp
@@ -0,0 +1,89 @@
+/*
+ * 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 "MediaTranscodingService"
+#include "MediaTranscodingService.h"
+
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <utils/Log.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+namespace media {
+
+using android::media::MediaTranscodingService;
+
+MediaTranscodingService::MediaTranscodingService() {
+ ALOGV("MediaTranscodingService is created");
+}
+
+MediaTranscodingService::~MediaTranscodingService() {
+ ALOGE("Should not be in ~MediaTranscodingService");
+}
+
+binder_status_t MediaTranscodingService::dump(int /* fd */, const char** /*args*/,
+ uint32_t /*numArgs*/) {
+ // TODO(hkuang): Add implementation.
+ return OK;
+}
+
+//static
+void MediaTranscodingService::instantiate() {
+ std::shared_ptr<MediaTranscodingService> service =
+ ::ndk::SharedRefBase::make<MediaTranscodingService>();
+ binder_status_t status =
+ AServiceManager_addService(service->asBinder().get(), getServiceName());
+ if (status != STATUS_OK) {
+ return;
+ }
+}
+
+Status MediaTranscodingService::registerClient(
+ const std::shared_ptr<ITranscodingServiceClient>& /*in_client*/,
+ int32_t* /*_aidl_return*/) {
+ // TODO(hkuang): Add implementation.
+ return Status::ok();
+}
+
+Status MediaTranscodingService::unregisterClient(int32_t /*clientId*/, bool* /*_aidl_return*/) {
+ // TODO(hkuang): Add implementation.
+ return Status::ok();
+}
+
+Status MediaTranscodingService::submitRequest(int32_t /*clientId*/,
+ const TranscodingRequest& /*request*/,
+ TranscodingJob* /*job*/, int32_t* /*_aidl_return*/) {
+ // TODO(hkuang): Add implementation.
+ return Status::ok();
+}
+
+Status MediaTranscodingService::cancelJob(int32_t /*in_clientId*/, int32_t /*in_jobId*/,
+ bool* /*_aidl_return*/) {
+ // TODO(hkuang): Add implementation.
+ return Status::ok();
+}
+
+Status MediaTranscodingService::getJobWithId(int32_t /*in_jobId*/, TranscodingJob* /*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
new file mode 100644
index 0000000..c2c30b9
--- /dev/null
+++ b/services/mediatranscoding/MediaTranscodingService.h
@@ -0,0 +1,62 @@
+/*
+ * 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_MEDIA_TRANSCODING_SERVICE_H
+#define ANDROID_MEDIA_TRANSCODING_SERVICE_H
+
+#include <aidl/android/media/BnMediaTranscodingService.h>
+#include <binder/IServiceManager.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;
+
+class MediaTranscodingService : public BnMediaTranscodingService {
+public:
+ MediaTranscodingService();
+ virtual ~MediaTranscodingService();
+
+ static void instantiate();
+
+ static const char* getServiceName() { return "media.transcoding"; }
+
+ Status registerClient(const std::shared_ptr<ITranscodingServiceClient>& in_client,
+ 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 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;
+
+ virtual inline binder_status_t dump(int /*fd*/, const char** /*args*/, uint32_t /*numArgs*/);
+
+private:
+};
+
+} // namespace media
+} // namespace android
+
+#endif // ANDROID_MEDIA_TRANSCODING_SERVICE_H
diff --git a/services/mediatranscoding/NOTICE b/services/mediatranscoding/NOTICE
new file mode 100644
index 0000000..9f46052
--- /dev/null
+++ b/services/mediatranscoding/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/services/mediatranscoding/OWNERS b/services/mediatranscoding/OWNERS
new file mode 100644
index 0000000..825c586
--- /dev/null
+++ b/services/mediatranscoding/OWNERS
@@ -0,0 +1,4 @@
+akersten@google.com
+hkuang@google.com
+lnilsson@google.com
+
diff --git a/services/mediatranscoding/main_mediatranscodingservice.cpp b/services/mediatranscoding/main_mediatranscodingservice.cpp
new file mode 100644
index 0000000..1978eb4
--- /dev/null
+++ b/services/mediatranscoding/main_mediatranscodingservice.cpp
@@ -0,0 +1,36 @@
+/*
+ * 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 <android-base/logging.h>
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+
+#include "MediaTranscodingService.h"
+
+using namespace android;
+
+int main(int argc __unused, char** argv) {
+ LOG(INFO) << "media transcoding service starting";
+
+ // TODO(hkuang): Start the service with libbinder_ndk.
+ strcpy(argv[0], "media.transcoding");
+ sp<ProcessState> proc(ProcessState::self());
+ sp<IServiceManager> sm = defaultServiceManager();
+ android::media::MediaTranscodingService::instantiate();
+
+ ProcessState::self()->startThreadPool();
+ IPCThreadState::self()->joinThreadPool();
+}
diff --git a/services/mediatranscoding/mediatranscoding.rc b/services/mediatranscoding/mediatranscoding.rc
new file mode 100644
index 0000000..2dc547f
--- /dev/null
+++ b/services/mediatranscoding/mediatranscoding.rc
@@ -0,0 +1,6 @@
+service media.transcoding /system/bin/mediatranscoding
+ class main
+ user media
+ group media
+ ioprio rt 4
+ writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index e6a8375..af8c67b 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -78,6 +78,11 @@
AAudioClientTracker::getInstance().registerClient(pid, client);
}
+bool AAudioService::isCallerInService() {
+ return mAudioClient.clientPid == IPCThreadState::self()->getCallingPid() &&
+ mAudioClient.clientUid == IPCThreadState::self()->getCallingUid();
+}
+
aaudio_handle_t AAudioService::openStream(const aaudio::AAudioStreamRequest &request,
aaudio::AAudioStreamConfiguration &configurationOutput) {
aaudio_result_t result = AAUDIO_OK;
@@ -105,8 +110,7 @@
if (sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE) {
// only trust audioserver for in service indication
bool inService = false;
- if (mAudioClient.clientPid == IPCThreadState::self()->getCallingPid() &&
- mAudioClient.clientUid == IPCThreadState::self()->getCallingUid()) {
+ if (isCallerInService()) {
inService = request.isInService();
}
serviceStream = new AAudioServiceStreamMMAP(*this, inService);
@@ -274,12 +278,14 @@
result = AAUDIO_ERROR_INVALID_STATE;
} else {
const pid_t ownerPid = IPCThreadState::self()->getCallingPid(); // TODO review
+ int32_t priority = isCallerInService()
+ ? kRealTimeAudioPriorityService : kRealTimeAudioPriorityClient;
serviceStream->setRegisteredThread(clientThreadId);
int err = android::requestPriority(ownerPid, clientThreadId,
- DEFAULT_AUDIO_PRIORITY, true /* isForApp */);
+ priority, true /* isForApp */);
if (err != 0) {
ALOGE("AAudioService::registerAudioThread(%d) failed, errno = %d, priority = %d",
- clientThreadId, errno, DEFAULT_AUDIO_PRIORITY);
+ clientThreadId, errno, priority);
result = AAUDIO_ERROR_INTERNAL;
}
}
diff --git a/services/oboeservice/AAudioService.h b/services/oboeservice/AAudioService.h
index d21b1cd..43a59c3 100644
--- a/services/oboeservice/AAudioService.h
+++ b/services/oboeservice/AAudioService.h
@@ -87,6 +87,10 @@
private:
+ /** @return true if the client is the audioserver
+ */
+ bool isCallerInService();
+
/**
* Lookup stream and then validate access to the stream.
* @param streamHandle
@@ -106,9 +110,10 @@
aaudio::AAudioStreamTracker mStreamTracker;
- enum constants {
- DEFAULT_AUDIO_PRIORITY = 2
- };
+ // TODO Extract the priority constants from services/audioflinger/Threads.cpp
+ // and share them with this code. Look for "kPriorityFastMixer".
+ static constexpr int32_t kRealTimeAudioPriorityClient = 2;
+ static constexpr int32_t kRealTimeAudioPriorityService = 3;
};
} /* namespace android */
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index 553754e..2753f1f 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -62,6 +62,7 @@
result << " InputPreset: " << getInputPreset() << "\n";
result << " Reference Count: " << mOpenCount << "\n";
result << " Session Id: " << getSessionId() << "\n";
+ result << " Privacy Sensitive: " << isPrivacySensitive() << "\n";
result << " Connected: " << mConnected.load() << "\n";
result << " Registered Streams:" << "\n";
result << AAudioServiceStreamShared::dumpHeader() << "\n";
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
index b05baa4..5bdb8eb 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
@@ -90,9 +90,14 @@
const audio_source_t source = (direction == AAUDIO_DIRECTION_INPUT)
? AAudioConvert_inputPresetToAudioSource(getInputPreset())
: AUDIO_SOURCE_DEFAULT;
- const audio_flags_mask_t flags = AUDIO_FLAG_LOW_LATENCY |
- AAudioConvert_allowCapturePolicyToAudioFlagsMask(getAllowedCapturePolicy());
-
+ audio_flags_mask_t flags;
+ if (direction == AAUDIO_DIRECTION_OUTPUT) {
+ flags = AUDIO_FLAG_LOW_LATENCY
+ | AAudioConvert_allowCapturePolicyToAudioFlagsMask(getAllowedCapturePolicy());
+ } else {
+ flags = AUDIO_FLAG_LOW_LATENCY
+ | AAudioConvert_privacySensitiveToAudioFlagsMask(isPrivacySensitive());
+ }
const audio_attributes_t attributes = {
.content_type = contentType,
.usage = usage,
diff --git a/services/oboeservice/Android.bp b/services/oboeservice/Android.bp
new file mode 100644
index 0000000..64d835b
--- /dev/null
+++ b/services/oboeservice/Android.bp
@@ -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.
+
+cc_library_shared {
+
+ name: "libaaudioservice",
+
+ srcs: [
+ "AAudioClientTracker.cpp",
+ "AAudioEndpointManager.cpp",
+ "AAudioMixer.cpp",
+ "AAudioService.cpp",
+ "AAudioServiceEndpoint.cpp",
+ "AAudioServiceEndpointCapture.cpp",
+ "AAudioServiceEndpointMMAP.cpp",
+ "AAudioServiceEndpointPlay.cpp",
+ "AAudioServiceEndpointShared.cpp",
+ "AAudioServiceStreamBase.cpp",
+ "AAudioServiceStreamMMAP.cpp",
+ "AAudioServiceStreamShared.cpp",
+ "AAudioStreamTracker.cpp",
+ "AAudioThread.cpp",
+ "SharedMemoryProxy.cpp",
+ "SharedRingBuffer.cpp",
+ "TimestampScheduler.cpp",
+ ],
+
+ cflags: [
+ "-Wno-unused-parameter",
+ "-Wall",
+ "-Werror",
+ ],
+
+ shared_libs: [
+ "libaaudio_internal",
+ "libaudioclient",
+ "libaudioflinger",
+ "libaudioutils",
+ "libbase",
+ "libbinder",
+ "libcutils",
+ "liblog",
+ "libmediautils",
+ "libutils",
+ ],
+
+ header_libs: [
+ "libaudiohal_headers",
+ ],
+
+ include_dirs: [
+ "frameworks/av/media/libnbaio/include_mono",
+ "frameworks/av/media/libnbaio/include",
+ ],
+}
diff --git a/services/oboeservice/Android.mk b/services/oboeservice/Android.mk
deleted file mode 100644
index 96ccebc..0000000
--- a/services/oboeservice/Android.mk
+++ /dev/null
@@ -1,58 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-# AAudio Service
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libaaudioservice
-LOCAL_MODULE_TAGS := optional
-
-LIBAAUDIO_DIR := ../../media/libaaudio
-LIBAAUDIO_SRC_DIR := $(LIBAAUDIO_DIR)/src
-
-LOCAL_C_INCLUDES := \
- $(TOPDIR)frameworks/av/services/audioflinger \
- $(call include-path-for, audio-utils) \
- frameworks/native/include \
- system/core/base/include \
- $(TOP)/frameworks/av/media/libaaudio/include \
- $(TOP)/frameworks/av/media/utils/include \
- frameworks/native/include \
- $(TOP)/external/tinyalsa/include \
- $(TOP)/frameworks/av/media/libaaudio/src
-
-LOCAL_SRC_FILES += \
- SharedMemoryProxy.cpp \
- SharedRingBuffer.cpp \
- AAudioClientTracker.cpp \
- AAudioEndpointManager.cpp \
- AAudioMixer.cpp \
- AAudioService.cpp \
- AAudioServiceEndpoint.cpp \
- AAudioServiceEndpointCapture.cpp \
- AAudioServiceEndpointMMAP.cpp \
- AAudioServiceEndpointPlay.cpp \
- AAudioServiceEndpointShared.cpp \
- AAudioServiceStreamBase.cpp \
- AAudioServiceStreamMMAP.cpp \
- AAudioServiceStreamShared.cpp \
- AAudioStreamTracker.cpp \
- TimestampScheduler.cpp \
- AAudioThread.cpp
-
-# LOCAL_CFLAGS += -fvisibility=hidden
-LOCAL_CFLAGS += -Wno-unused-parameter
-LOCAL_CFLAGS += -Wall -Werror
-
-LOCAL_SHARED_LIBRARIES := \
- libaaudio_internal \
- libaudioflinger \
- libaudioclient \
- libbinder \
- libcutils \
- libmediautils \
- libutils \
- liblog
-
-include $(BUILD_SHARED_LIBRARY)
-
-
diff --git a/services/soundtrigger/Android.bp b/services/soundtrigger/Android.bp
deleted file mode 100644
index 1bbd591..0000000
--- a/services/soundtrigger/Android.bp
+++ /dev/null
@@ -1,53 +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.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 68d54c7..0000000
--- a/services/soundtrigger/SoundTriggerHalHidl.cpp
+++ /dev/null
@@ -1,839 +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;
-}
-
-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<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;
-}
-
-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 fb9e39e..0000000
--- a/services/soundtrigger/SoundTriggerHalHidl.h
+++ /dev/null
@@ -1,214 +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.1/ISoundTriggerHw.h>
-#include <android/hardware/soundtrigger/2.2/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;
-
-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);
-
- // 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);
-
- 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<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 0183ece..0000000
--- a/services/soundtrigger/SoundTriggerHalInterface.h
+++ /dev/null
@@ -1,88 +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;
-
-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 377d30b..0000000
--- a/services/soundtrigger/SoundTriggerHwService.cpp
+++ /dev/null
@@ -1,1118 +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 {
-
-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->pointer() == NULL) {
- eventMemory.clear();
- return eventMemory;
- }
- memcpy(eventMemory->pointer(), 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->pointer() == NULL) {
- eventMemory.clear();
- return eventMemory;
- }
- memcpy(eventMemory->pointer(), 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->pointer() == NULL) {
- eventMemory.clear();
- return eventMemory;
- }
- *((sound_trigger_service_state_t *)eventMemory->pointer()) = 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;
- }
-
- struct sound_trigger_sound_model *sound_model =
- (struct sound_trigger_sound_model *)modelMemory->pointer();
-
- 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) ||
- modelMemory->size() < sound_model->data_offset ||
- sound_model->data_size > (modelMemory->size() - 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;
- }
-
- struct sound_trigger_recognition_config *config =
- (struct sound_trigger_recognition_config *)dataMemory->pointer();
-
- if (config->data_offset < sizeof(struct sound_trigger_recognition_config) ||
- config->data_size > (UINT_MAX - config->data_offset) ||
- dataMemory->size() < config->data_offset ||
- config->data_size > (dataMemory->size() - 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);
-}
-
-void SoundTriggerHwService::Module::onCallbackEvent(const sp<CallbackEvent>& event)
-{
- ALOGV("onCallbackEvent type %d", event->mType);
-
- sp<IMemory> eventMemory = event->mMemory;
-
- if (eventMemory == 0 || eventMemory->pointer() == 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->pointer();
- {
- 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->pointer();
- {
- 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);
-}
-
-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;
-
- if (eventMemory == 0 || eventMemory->pointer() == 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 43ad611..0000000
--- a/services/soundtrigger/SoundTriggerHwService.h
+++ /dev/null
@@ -1,254 +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);
-
- 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 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 f5b4b59..0000000
--- a/soundtrigger/ISoundTrigger.cpp
+++ /dev/null
@@ -1,203 +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,
-};
-
-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;
- }
-
-};
-
-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;
- }
- 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 9708ea7..0000000
--- a/soundtrigger/SoundTrigger.cpp
+++ /dev/null
@@ -1,300 +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);
-}
-
-// BpSoundTriggerClient
-void SoundTrigger::onRecognitionEvent(const sp<IMemory>& eventMemory)
-{
- Mutex::Autolock _l(mLock);
- if (eventMemory == 0 || eventMemory->pointer() == NULL) {
- return;
- }
-
- if (mCallback != 0) {
- mCallback->onRecognitionEvent(
- (struct sound_trigger_recognition_event *)eventMemory->pointer());
- }
-}
-
-void SoundTrigger::onSoundModelEvent(const sp<IMemory>& eventMemory)
-{
- Mutex::Autolock _l(mLock);
- if (eventMemory == 0 || eventMemory->pointer() == NULL) {
- return;
- }
-
- if (mCallback != 0) {
- mCallback->onSoundModelEvent(
- (struct sound_trigger_model_event *)eventMemory->pointer());
- }
-}
-
-void SoundTrigger::onServiceStateChange(const sp<IMemory>& eventMemory)
-{
- Mutex::Autolock _l(mLock);
- if (eventMemory == 0 || eventMemory->pointer() == NULL) {
- return;
- }
-
- if (mCallback != 0) {
- mCallback->onServiceStateChange(
- *((sound_trigger_service_state_t *)eventMemory->pointer()));
- }
-}
-
-//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
diff --git a/tools/OWNERS b/tools/OWNERS
deleted file mode 100644
index f9cb567..0000000
--- a/tools/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-gkasten@google.com
diff --git a/tools/mainline_hook.sh b/tools/mainline_hook.sh
new file mode 100755
index 0000000..58afb49
--- /dev/null
+++ b/tools/mainline_hook.sh
@@ -0,0 +1,84 @@
+#!/bin/bash
+LOCAL_DIR="$( dirname "${BASH_SOURCE}" )"
+
+MAINLINE_FRAMEWORKS_AV_PATHS=(
+ media/extractors/
+ media/codec2/components/
+ media/libstagefright/codecs/amrnb
+ media/libstagefright/codecs/amrwb
+ media/libstagefright/codecs/amrwbenc
+ media/libstagefright/codecs/common
+ media/libstagefright/codecs/mp3dec
+ media/libstagefright/codecs/m4v_h263
+ media/libstagefright/flac/dec
+ media/libstagefright/mpeg2ts
+)
+
+MAINLINE_EXTERNAL_PROJECTS=(
+ external/aac
+ external/flac
+ external/libaac
+ external/libaom
+ external/libavc
+ external/libgav1
+ external/libgsm
+ external/libhevc
+ external/libmpeg2
+ external/libopus
+ external/libvpx
+ external/libxaac
+ external/sonivox
+ external/tremolo
+)
+
+DEV_BRANCH=qt-aml-media-dev
+RED=$(tput setaf 1)
+NORMAL=$(tput sgr0)
+WARNING_FULL="${RED}Please upload this change in ${DEV_BRANCH} unless it is restricted
+from mainline release until next dessert release. Low/moderate security bugs
+are restricted this way.${NORMAL}"
+WARNING_PARTIAL="${RED}It looks like your change has mainline and non-mainline changes;
+Consider separating them into two separate CLs -- one for mainline files,
+one for non-mainline files.${NORMAL}"
+PWD=`pwd`
+
+if git branch -vv | grep -q -P "^\*[^\[]+\[goog/qt-aml-media-dev"; then
+ # Change appears to be in mainline dev branch
+ exit 0
+fi
+
+for path in "${MAINLINE_EXTERNAL_PROJECTS[@]}"; do
+ if [[ $PWD =~ $path ]]; then
+ echo -e "${RED}The source of truth for '$path' is in ${DEV_BRANCH}.${NORMAL}"
+ echo -e ${WARNING_FULL}
+ exit 1
+ fi
+done
+
+if [[ ! $PWD =~ frameworks/av ]]; then
+ exit 0
+fi
+
+mainline_count=0
+total_count=0
+echo
+while read -r file ; do
+ (( total_count++ ))
+ for path in "${MAINLINE_FRAMEWORKS_AV_PATHS[@]}"; do
+ if [[ $file =~ ^$path ]]; then
+ echo -e "${RED}The source of truth for '$file' is in ${DEV_BRANCH}.${NORMAL}"
+ (( mainline_count++ ))
+ break
+ fi
+ done
+done < <(git show --name-only --pretty=format: $1 | grep -- "$2")
+
+if (( mainline_count != 0 )); then
+ if (( mainline_count == total_count )); then
+ echo -e ${WARNING_FULL}
+ else
+ echo -e ${WARNING_PARTIAL}
+ fi
+ exit 1
+fi
+exit 0
diff --git a/tools/resampler_tools/Android.bp b/tools/resampler_tools/Android.bp
deleted file mode 100644
index 7549359..0000000
--- a/tools/resampler_tools/Android.bp
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2005 The Android Open Source Project
-//
-// Android.mk for resampler_tools
-//
-
-cc_binary_host {
- name: "fir",
-
- srcs: ["fir.cpp"],
-
- cflags: [
- "-Werror",
- "-Wall",
- ],
-}
diff --git a/tools/resampler_tools/OWNERS b/tools/resampler_tools/OWNERS
deleted file mode 100644
index b4a6798..0000000
--- a/tools/resampler_tools/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-hunga@google.com
diff --git a/tools/resampler_tools/fir.cpp b/tools/resampler_tools/fir.cpp
deleted file mode 100644
index fe4d212..0000000
--- a/tools/resampler_tools/fir.cpp
+++ /dev/null
@@ -1,318 +0,0 @@
-/*
- * Copyright (C) 2007 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 <math.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-
-static inline double sinc(double x) {
- if (fabs(x) == 0.0f) return 1.0f;
- return sin(x) / x;
-}
-
-static inline double sqr(double x) {
- return x*x;
-}
-
-static inline int64_t toint(double x, int64_t maxval) {
- int64_t v;
-
- v = static_cast<int64_t>(floor(x * maxval + 0.5));
- if (v >= maxval) {
- return maxval - 1; // error!
- }
- return v;
-}
-
-static double I0(double x) {
- // from the Numerical Recipes in C p. 237
- double ax,ans,y;
- ax=fabs(x);
- if (ax < 3.75) {
- y=x/3.75;
- y*=y;
- ans=1.0+y*(3.5156229+y*(3.0899424+y*(1.2067492
- +y*(0.2659732+y*(0.360768e-1+y*0.45813e-2)))));
- } else {
- y=3.75/ax;
- ans=(exp(ax)/sqrt(ax))*(0.39894228+y*(0.1328592e-1
- +y*(0.225319e-2+y*(-0.157565e-2+y*(0.916281e-2
- +y*(-0.2057706e-1+y*(0.2635537e-1+y*(-0.1647633e-1
- +y*0.392377e-2))))))));
- }
- return ans;
-}
-
-static double kaiser(int k, int N, double beta) {
- if (k < 0 || k > N)
- return 0;
- return I0(beta * sqrt(1.0 - sqr((2.0*k)/N - 1.0))) / I0(beta);
-}
-
-static void usage(char* name) {
- fprintf(stderr,
- "usage: %s [-h] [-d] [-D] [-s sample_rate] [-c cut-off_frequency] [-n half_zero_crossings]"
- " [-f {float|fixed|fixed16}] [-b beta] [-v dBFS] [-l lerp]\n"
- " %s [-h] [-d] [-D] [-s sample_rate] [-c cut-off_frequency] [-n half_zero_crossings]"
- " [-f {float|fixed|fixed16}] [-b beta] [-v dBFS] -p M/N\n"
- " -h this help message\n"
- " -d debug, print comma-separated coefficient table\n"
- " -D generate extra declarations\n"
- " -p generate poly-phase filter coefficients, with sample increment M/N\n"
- " -s sample rate (48000)\n"
- " -c cut-off frequency (20478)\n"
- " -n number of zero-crossings on one side (8)\n"
- " -l number of lerping bits (4)\n"
- " -m number of polyphases (related to -l, default 16)\n"
- " -f output format, can be fixed, fixed16, or float (fixed)\n"
- " -b kaiser window parameter beta (7.865 [-80dB])\n"
- " -v attenuation in dBFS (0)\n",
- name, name
- );
- exit(0);
-}
-
-int main(int argc, char** argv)
-{
- // nc is the number of bits to store the coefficients
- int nc = 32;
- bool polyphase = false;
- unsigned int polyM = 160;
- unsigned int polyN = 147;
- bool debug = false;
- double Fs = 48000;
- double Fc = 20478;
- double atten = 1;
- int format = 0; // 0=fixed, 1=float
- bool declarations = false;
-
- // in order to keep the errors associated with the linear
- // interpolation of the coefficients below the quantization error
- // we must satisfy:
- // 2^nz >= 2^(nc/2)
- //
- // for 16 bit coefficients that would be 256
- //
- // note that increasing nz only increases memory requirements,
- // but doesn't increase the amount of computation to do.
- //
- //
- // see:
- // Smith, J.O. Digital Audio Resampling Home Page
- // https://ccrma.stanford.edu/~jos/resample/, 2011-03-29
- //
-
- // | 0.1102*(A - 8.7) A > 50
- // beta = | 0.5842*(A - 21)^0.4 + 0.07886*(A - 21) 21 <= A <= 50
- // | 0 A < 21
- // with A is the desired stop-band attenuation in dBFS
- //
- // for eg:
- //
- // 30 dB 2.210
- // 40 dB 3.384
- // 50 dB 4.538
- // 60 dB 5.658
- // 70 dB 6.764
- // 80 dB 7.865
- // 90 dB 8.960
- // 100 dB 10.056
- double beta = 7.865;
-
- // 2*nzc = (A - 8) / (2.285 * dw)
- // with dw the transition width = 2*pi*dF/Fs
- //
- int nzc = 8;
-
- /*
- * Example:
- * 44.1 KHz to 48 KHz resampling
- * 100 dB rejection above 28 KHz
- * (the spectrum will fold around 24 KHz and we want 100 dB rejection
- * at the point where the folding reaches 20 KHz)
- * ...___|_____
- * | \|
- * | ____/|\____
- * |/alias| \
- * ------/------+------\---------> KHz
- * 20 24 28
- *
- * Transition band 8 KHz, or dw = 1.0472
- *
- * beta = 10.056
- * nzc = 20
- */
-
- int M = 1 << 4; // number of phases for interpolation
- int ch;
- while ((ch = getopt(argc, argv, ":hds:c:n:f:l:m:b:p:v:z:D")) != -1) {
- switch (ch) {
- case 'd':
- debug = true;
- break;
- case 'D':
- declarations = true;
- break;
- case 'p':
- if (sscanf(optarg, "%u/%u", &polyM, &polyN) != 2) {
- usage(argv[0]);
- }
- polyphase = true;
- break;
- case 's':
- Fs = atof(optarg);
- break;
- case 'c':
- Fc = atof(optarg);
- break;
- case 'n':
- nzc = atoi(optarg);
- break;
- case 'm':
- M = atoi(optarg);
- break;
- case 'l':
- M = 1 << atoi(optarg);
- break;
- case 'f':
- if (!strcmp(optarg, "fixed")) {
- format = 0;
- }
- else if (!strcmp(optarg, "fixed16")) {
- format = 0;
- nc = 16;
- }
- else if (!strcmp(optarg, "float")) {
- format = 1;
- }
- else {
- usage(argv[0]);
- }
- break;
- case 'b':
- beta = atof(optarg);
- break;
- case 'v':
- atten = pow(10, -fabs(atof(optarg))*0.05 );
- break;
- case 'h':
- default:
- usage(argv[0]);
- break;
- }
- }
-
- // cut off frequency ratio Fc/Fs
- double Fcr = Fc / Fs;
-
- // total number of coefficients (one side)
-
- const int N = M * nzc;
-
- // lerp (which is most useful if M is a power of 2)
-
- int nz = 0; // recalculate nz as the bits needed to represent M
- for (int i = M-1 ; i; i>>=1, nz++);
- // generate the right half of the filter
- if (!debug) {
- printf("// cmd-line:");
- for (int i=0 ; i<argc ; i++) {
- printf(" %s", argv[i]);
- }
- printf("\n");
- if (declarations) {
- if (!polyphase) {
- printf("const int32_t RESAMPLE_FIR_SIZE = %d;\n", N);
- printf("const int32_t RESAMPLE_FIR_INT_PHASES = %d;\n", M);
- printf("const int32_t RESAMPLE_FIR_NUM_COEF = %d;\n", nzc);
- } else {
- printf("const int32_t RESAMPLE_FIR_SIZE = %d;\n", 2*nzc*polyN);
- printf("const int32_t RESAMPLE_FIR_NUM_COEF = %d;\n", 2*nzc);
- }
- if (!format) {
- printf("const int32_t RESAMPLE_FIR_COEF_BITS = %d;\n", nc);
- }
- printf("\n");
- printf("static %s resampleFIR[] = {", !format ? "int32_t" : "float");
- }
- }
-
- if (!polyphase) {
- for (int i=0 ; i<=M ; i++) { // an extra set of coefs for interpolation
- for (int j=0 ; j<nzc ; j++) {
- int ix = j*M + i;
- double x = (2.0 * M_PI * ix * Fcr) / M;
- double y = kaiser(ix+N, 2*N, beta) * sinc(x) * 2.0 * Fcr;
- y *= atten;
-
- if (!debug) {
- if (j == 0)
- printf("\n ");
- }
- if (!format) {
- int64_t yi = toint(y, 1ULL<<(nc-1));
- if (nc > 16) {
- printf("0x%08x,", int32_t(yi));
- } else {
- printf("0x%04x,", int32_t(yi)&0xffff);
- }
- } else {
- printf("%.9g%s", y, debug ? "," : "f,");
- }
- if (j != nzc-1) {
- printf(" ");
- }
- }
- }
- } else {
- for (unsigned int j=0 ; j<polyN ; j++) {
- // calculate the phase
- double p = ((polyM*j) % polyN) / double(polyN);
- if (!debug) printf("\n ");
- else printf("\n");
- // generate a FIR per phase
- for (int i=-nzc ; i<nzc ; i++) {
- double x = 2.0 * M_PI * Fcr * (i + p);
- double y = kaiser(i+N, 2*N, beta) * sinc(x) * 2.0 * Fcr;;
- y *= atten;
- if (!format) {
- int64_t yi = toint(y, 1ULL<<(nc-1));
- if (nc > 16) {
- printf("0x%08x,", int32_t(yi));
- } else {
- printf("0x%04x,", int32_t(yi)&0xffff);
- }
- } else {
- printf("%.9g%s", y, debug ? "," : "f,");
- }
-
- if (i != nzc-1) {
- printf(" ");
- }
- }
- }
- }
-
- if (!debug && declarations) {
- printf("\n};");
- }
- printf("\n");
- return 0;
-}
-
-// http://www.csee.umbc.edu/help/sound/AFsp-V2R1/html/audio/ResampAudio.html